MainViewController.m 134 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409
  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 "MainViewController.h"
  17. #import <objc/runtime.h>
  18. #import <FirebaseCore/FIRApp.h>
  19. #import <FirebaseCore/FIRAppInternal.h>
  20. #import <FirebaseCore/FIRAppAssociationRegistration.h>
  21. #import "AppManager.h"
  22. #import "AuthCredentials.h"
  23. #import "FIRAdditionalUserInfo.h"
  24. #import "FIROAuthProvider.h"
  25. #import "FIRPhoneAuthCredential.h"
  26. #import "FIRPhoneAuthProvider.h"
  27. #import "FIRAuthTokenResult.h"
  28. #import "FirebaseAuth.h"
  29. #import "CustomTokenDataEntryViewController.h"
  30. #import "FacebookAuthProvider.h"
  31. #import "GoogleAuthProvider.h"
  32. #import "SettingsViewController.h"
  33. #import "StaticContentTableViewManager.h"
  34. #import "UIViewController+Alerts.h"
  35. #import "UserInfoViewController.h"
  36. #import "UserTableViewCell.h"
  37. NS_ASSUME_NONNULL_BEGIN
  38. /** @typedef textInputCompletionBlock
  39. @brief The type of callback used to report text input prompt results.
  40. */
  41. typedef void (^textInputCompletionBlock)(NSString *_Nullable userInput);
  42. /** @typedef testAutomationCallback
  43. @brief The type of callback used when automatically testing an API.
  44. */
  45. typedef void (^testAutomationCallback)(NSError *_Nullable error);
  46. /** @var kTokenGetButtonText
  47. @brief The text of the "Get Token" button.
  48. */
  49. static NSString *const kTokenGetButtonText = @"Get Token";
  50. /** @var kGetTokenResultButtonText
  51. @brief The text of the "Get Token Result" button.
  52. */
  53. static NSString *const kGetTokenResultButtonText = @"Get Token Result";
  54. /** @var kGetTokenResultForceButtonText
  55. @brief The text of the "Force Token Result" button.
  56. */
  57. static NSString *const kGetTokenResultForceButtonText = @"Force Token Result";
  58. /** @var kTokenRefreshButtonText
  59. @brief The text of the "Refresh Token" button.
  60. */
  61. static NSString *const kTokenRefreshButtonText = @"Force Refresh Token";
  62. /** @var kTokenRefreshedAlertTitle
  63. @brief The title of the "Token Refreshed" alert.
  64. */
  65. static NSString *const kTokenRefreshedAlertTitle = @"Token";
  66. /** @var kTokenRefreshErrorAlertTitle
  67. @brief The title of the "Token Refresh error" alert.
  68. */
  69. static NSString *const kTokenRefreshErrorAlertTitle = @"Get Token Error";
  70. /** @var kSettingsButtonTextDesription
  71. @brief The description for the text of the "Settings" button.
  72. */
  73. static NSString *const kSettingsButtonTextDesription =
  74. @"The button text to open sample app settings";
  75. /** @var kUserInfoButtonText
  76. @brief The text of the "Show User Info" button.
  77. */
  78. static NSString *const kUserInfoButtonText = @"[Show User Info]";
  79. /** @var kSetImageURLText
  80. @brief The text of the "Set Photo url" button.
  81. */
  82. static NSString *const kSetPhotoURLText = @"Set Photo url";
  83. /** @var kSignInGoogleButtonText
  84. @brief The text of the "Google SignIn" button.
  85. */
  86. static NSString *const kSignInGoogleButtonText = @"Sign in with Google";
  87. /** @var kSignInWithEmailLink
  88. @brief The text of the "Sign in with Email Link" button.
  89. */
  90. static NSString *const kSignInWithEmailLink = @"Sign in with Email Link";
  91. /** @var kVerifyEmailLinkAccount
  92. @brief The text of the "Verify Email-link Account" button.
  93. */
  94. static NSString *const kVerifyEmailLinkAccount = @"Verify Email-link Account";
  95. /** @var kSendEmailSignInLink
  96. @brief The text of the "Send Email SignIn link" button
  97. */
  98. static NSString *const kSendEmailSignInLink = @"Send Email Sign in Link";
  99. /** @var kSignInAndRetrieveGoogleButtonText
  100. @brief The text of the "Sign in with Google and retrieve data" button.
  101. */
  102. static NSString *const kSignInGoogleAndRetrieveDataButtonText =
  103. @"Sign in with Google and retrieve data";
  104. /** @var kSignInFacebookButtonText
  105. @brief The text of the "Facebook SignIn" button.
  106. */
  107. static NSString *const kSignInFacebookButtonText = @"Sign in with Facebook";
  108. /** @var kSignInFacebookAndRetrieveDataButtonText
  109. @brief The text of the "Facebook SignIn and retrieve data" button.
  110. */
  111. static NSString *const kSignInFacebookAndRetrieveDataButtonText =
  112. @"Sign in with Facebook and retrieve data";
  113. /** @var kSignInEmailPasswordButtonText
  114. @brief The text of the "Email/Password SignIn" button.
  115. */
  116. static NSString *const kSignInEmailPasswordButtonText = @"Sign in with Email/Password";
  117. /** @var kSignInEmailPasswordAuthDataResultButtonText
  118. @brief The text of the "Email/Password SignIn (AuthDataResult)" button.
  119. */
  120. static NSString *const kSignInEmailPasswordAuthDataResultButtonText =
  121. @"Sign in with Email/Password (AuthDataReult)";
  122. /** @var kSignInWithCustomTokenButtonText
  123. @brief The text of the "Sign In (BYOAuth)" button.
  124. */
  125. static NSString *const kSignInWithCustomTokenButtonText = @"Sign In (BYOAuth)";
  126. /** @var kSignInWithCustomAuthResultTokenButtonText
  127. @brief The text of the "Sign In with Custom Token (Auth Result)" button.
  128. */
  129. static NSString *const kSignInWithCustomAuthResultTokenButtonText = @"Sign In with Custom Token"
  130. " (Auth Result)";
  131. /** @var kSignInAnonymouslyButtonText
  132. @brief The text of the "Sign In Anonymously" button.
  133. */
  134. static NSString *const kSignInAnonymouslyButtonText = @"Sign In Anonymously";
  135. /** @var kSignInAnonymouslyWithAuthResultButtonText
  136. @brief The text of the "Sign In Anonymously (AuthDataResult)" button.
  137. */
  138. static NSString *const kSignInAnonymouslyWithAuthResultButtonText =
  139. @"Sign In Anonymously (AuthDataResult)";
  140. /** @var kSignedInAlertTitle
  141. @brief The text of the "Sign In Succeeded" alert.
  142. */
  143. static NSString *const kSignedInAlertTitle = @"Signed In";
  144. /** @var kSignInErrorAlertTitle
  145. @brief The text of the "Sign In Encountered an Error" alert.
  146. */
  147. static NSString *const kSignInErrorAlertTitle = @"Sign-In Error";
  148. /** @var kSignOutButtonText
  149. @brief The text of the "Sign Out" button.
  150. */
  151. static NSString *const kSignOutButtonText = @"Sign Out";
  152. /** @var kDeleteAccountText
  153. @brief The text of the "Delete Account" button.
  154. */
  155. static NSString *const kDeleteUserText = @"Delete Account";
  156. /** @var kReauthenticateGoogleText
  157. @brief The text of the "Reathenticate Google" button.
  158. */
  159. static NSString *const kReauthenticateGoogleText = @"Reauthenticate Google";
  160. /** @var kReauthenticateGoogleAndRetrieveDataText
  161. @brief The text of the "Reathenticate Google and retrieve data" button.
  162. */
  163. static NSString *const kReauthenticateGoogleAndRetrieveDataText =
  164. @"Reauthenticate Google and retrieve data";
  165. /** @var kReauthenticateFBText
  166. @brief The text of the "Reathenticate Facebook" button.
  167. */
  168. static NSString *const kReauthenticateFBText = @"Reauthenticate FB";
  169. /** @var kReauthenticateFBAndRetrieveDataText
  170. @brief The text of the "Reathenticate Facebook and retrieve data" button.
  171. */
  172. static NSString *const kReauthenticateFBAndRetrieveDataText =
  173. @"Reauthenticate FB and retrieve data";
  174. /** @var kReAuthenticateEmail
  175. @brief The text of the "Reathenticate Email" button.
  176. */
  177. static NSString *const kReauthenticateEmailText = @"Reauthenticate Email/Password";
  178. /** @var kOKButtonText
  179. @brief The text of the "OK" button for the Sign In result dialogs.
  180. */
  181. static NSString *const kOKButtonText = @"OK";
  182. /** @var kSetDisplayNameTitle
  183. @brief The title of the "Set Display Name" error dialog.
  184. */
  185. static NSString *const kSetDisplayNameTitle = @"Set Display Name";
  186. /** @var kUpdateEmailText
  187. @brief The title of the "Update Email" button.
  188. */
  189. static NSString *const kUpdateEmailText = @"Update Email";
  190. /** @var kUpdatePasswordText
  191. @brief The title of the "Update Password" button.
  192. */
  193. static NSString *const kUpdatePasswordText = @"Update Password";
  194. /** @var kUpdatePhoneNumber
  195. @brief The title of the "Update Photo" button.
  196. */
  197. static NSString *const kUpdatePhoneNumber = @"Update Phone Number";
  198. /** @var kLinkPhoneNumber
  199. @brief The title of the "Link phone" button.
  200. */
  201. static NSString *const kLinkPhoneNumber = @"Link Phone Number";
  202. /** @var kUnlinkPhone
  203. @brief The title of the "Unlink Phone" button for unlinking phone auth provider.
  204. */
  205. static NSString *const kUnlinkPhoneNumber = @"Unlink Phone Number";
  206. /** @var kReloadText
  207. @brief The title of the "Reload User" button.
  208. */
  209. static NSString *const kReloadText = @"Reload User";
  210. /** @var kLinkWithGoogleText
  211. @brief The title of the "Link with Google" button.
  212. */
  213. static NSString *const kLinkWithGoogleText = @"Link with Google";
  214. /** @var kLinkWithGoogleAndRetrieveDataText
  215. @brief The title of the "Link with Google and retrieve data" button.
  216. */
  217. static NSString *const kLinkWithGoogleAndRetrieveDataText = @"Link with Google and retrieve data";
  218. /** @var kLinkWithFacebookText
  219. @brief The title of the "Link with Facebook Account" button.
  220. */
  221. static NSString *const kLinkWithFacebookText = @"Link with Facebook";
  222. /** @var kLinkWithFacebookAndRetrieveDataText
  223. @brief The title of the "Link with Facebook and retrieve data" button.
  224. */
  225. static NSString *const kLinkWithFacebookAndRetrieveDataText =
  226. @"Link with Facebook and retrieve data";
  227. /** @var kLinkWithEmailPasswordText
  228. @brief The title of the "Link with Email/Password Account" button.
  229. */
  230. static NSString *const kLinkWithEmailPasswordText = @"Link with Email/Password";
  231. /** @var kUnlinkTitle
  232. @brief The text of the "Unlink from Provider" error Dialog.
  233. */
  234. static NSString *const kUnlinkTitle = @"Unlink from Provider";
  235. /** @var kUnlinkFromGoogle
  236. @brief The text of the "Unlink from Google" button.
  237. */
  238. static NSString *const kUnlinkFromGoogle = @"Unlink from Google";
  239. /** @var kUnlinkFromFacebook
  240. @brief The text of the "Unlink from Facebook" button.
  241. */
  242. static NSString *const kUnlinkFromFacebook = @"Unlink from Facebook";
  243. /** @var kUnlinkFromEmailPassword
  244. @brief The text of the "Unlink from Google" button.
  245. */
  246. static NSString *const kUnlinkFromEmailPassword = @"Unlink from Email/Password";
  247. /** @var kGetProvidersForEmail
  248. @brief The text of the "Get Provider IDs for Email" button.
  249. */
  250. static NSString *const kGetProvidersForEmail = @"Get Provider IDs for Email";
  251. /** @var kGetAllSignInMethodsForEmail
  252. @brief The text of the "Get sign-in methods for Email" button.
  253. */
  254. static NSString *const kGetAllSignInMethodsForEmail = @"Get Sign-in methods for Email";
  255. /** @var kActionCodeTypeDescription
  256. @brief The description of the "Action Type" entry.
  257. */
  258. static NSString *const kActionCodeTypeDescription = @"Action Type";
  259. /** @var kContinueURLDescription
  260. @brief The description of the "Continue URL" entry.
  261. */
  262. static NSString *const kContinueURLDescription = @"Continue URL";
  263. /** @var kRequestVerifyEmail
  264. @brief The text of the "Request Verify Email Link" button.
  265. */
  266. static NSString *const kRequestVerifyEmail = @"Request Verify Email Link";
  267. /** @var kRequestPasswordReset
  268. @brief The text of the "Email Password Reset" button.
  269. */
  270. static NSString *const kRequestPasswordReset = @"Send Password Reset Email";
  271. /** @var kResetPassword
  272. @brief The text of the "Password Reset" button.
  273. */
  274. static NSString *const kResetPassword = @"Reset Password";
  275. /** @var kCheckActionCode
  276. @brief The text of the "Check action code" button.
  277. */
  278. static NSString *const kCheckActionCode = @"Check action code";
  279. /** @var kApplyActionCode
  280. @brief The text of the "Apply action code" button.
  281. */
  282. static NSString *const kApplyActionCode = @"Apply action code";
  283. /** @var kVerifyPasswordResetCode
  284. @brief The text of the "Verify password reset code" button.
  285. */
  286. static NSString *const kVerifyPasswordResetCode = @"Verify password reset code";
  287. /** @var kSectionTitleSettings
  288. @brief The text for the title of the "Settings" section.
  289. */
  290. static NSString *const kSectionTitleSettings = @"SETTINGS";
  291. /** @var kSectionTitleSignIn
  292. @brief The text for the title of the "Sign-In" section.
  293. */
  294. static NSString *const kSectionTitleSignIn = @"SIGN-IN";
  295. /** @var kSectionTitleReauthenticate
  296. @brief The text for the title of the "Reauthenticate" section.
  297. */
  298. static NSString *const kSectionTitleReauthenticate = @"REAUTHENTICATE";
  299. /** @var kSectionTitleTokenActions
  300. @brief The text for the title of the "Token Actions" section.
  301. */
  302. static NSString *const kSectionTitleTokenActions = @"TOKEN ACTIONS";
  303. /** @var kSectionTitleEditUser
  304. @brief The text for the title of the "Edit User" section.
  305. */
  306. static NSString *const kSectionTitleEditUser = @"EDIT USER";
  307. /** @var kSectionTitleLinkUnlinkAccount
  308. @brief The text for the title of the "Link/Unlink account" section.
  309. */
  310. static NSString *const kSectionTitleLinkUnlinkAccounts = @"LINK/UNLINK ACCOUNT";
  311. /** @var kSectionTitleUserActions
  312. @brief The text for the title of the "User Actions" section.
  313. */
  314. static NSString *const kSectionTitleUserActions = @"USER ACTIONS";
  315. /** @var kSectionTitleOOBAction
  316. @brief The text for the title of the "OOB Actions" section.
  317. */
  318. static NSString *const kSectionTitleOOBActions = @"OOB ACTIONS";
  319. /** @var kSectionTitleUserDetails
  320. @brief The text for the title of the "User Details" section.
  321. */
  322. static NSString *const kSectionTitleUserDetails = @"SIGNED-IN USER DETAILS";
  323. /** @var kSectionTitleListeners
  324. @brief The title for the table view section dedicated to auth state did change listeners.
  325. */
  326. static NSString *const kSectionTitleListeners = @"Listeners";
  327. /** @var kAddAuthStateListenerTitle
  328. @brief The title for the table view row which adds a block to the auth state did change
  329. listeners.
  330. */
  331. static NSString *const kAddAuthStateListenerTitle = @"Add Auth State Change Listener";
  332. /** @var kRemoveAuthStateListenerTitle
  333. @brief The title for the table view row which removes a block to the auth state did change
  334. listeners.
  335. */
  336. static NSString *const kRemoveAuthStateListenerTitle = @"Remove Last Auth State Change Listener";
  337. /** @var kAddIDTokenListenerTitle
  338. @brief The title for the table view row which adds a block to the ID token did change
  339. listeners.
  340. */
  341. static NSString *const kAddIDTokenListenerTitle = @"Add ID Token Change Listener";
  342. /** @var kRemoveIDTokenListenerTitle
  343. @brief The title for the table view row which removes a block to the ID token did change
  344. listeners.
  345. */
  346. static NSString *const kRemoveIDTokenListenerTitle = @"Remove Last ID Token Change Listener";
  347. /** @var kSectionTitleApp
  348. @brief The text for the title of the "App" section.
  349. */
  350. static NSString *const kSectionTitleApp = @"APP";
  351. /** @var kUpdateCurrentUserFromSavedTitle
  352. @brief The text of the "Upgrade to saved user" button.
  353. */
  354. static NSString *const kUpdateCurrentUserFromSavedTitle = @"Upgrade to saved user";
  355. /** @var kCreateUserTitle
  356. @brief The text of the "Create User" button.
  357. */
  358. static NSString *const kCreateUserTitle = @"Create User";
  359. /** @var kCreateUserAuthDataResultTitle
  360. @brief The text of the "Create User (AuthDataResult)" button.
  361. */
  362. static NSString *const kCreateUserAuthDataResultTitle = @"Create User (AuthDataResult)";
  363. /** @var kDeleteAppTitle
  364. @brief The text of the "Delete App" button.
  365. */
  366. static NSString *const kDeleteAppTitle = @"Delete App";
  367. /** @var kTimeAuthInitTitle
  368. @brief The text of the "Time Auth Initialization" button.
  369. */
  370. static NSString *const kTimeAuthInitTitle = @"Time Auth Initialization";
  371. /** @var kSectionTitleManualTests
  372. @brief The section title for automated manual tests.
  373. */
  374. static NSString *const kSectionTitleManualTests = @"Automated (Manual) Tests";
  375. /** @var kAutoBYOAuthTitle
  376. @brief The button title for automated BYOAuth operation.
  377. */
  378. static NSString *const kAutoBYOAuthTitle = @"BYOAuth";
  379. /** @var kAutoSignInGoogle
  380. @brief The button title for automated Google sign-in operation.
  381. */
  382. static NSString *const kAutoSignInGoogle = @"Sign In With Google";
  383. /** @var kAutoSignInFacebook
  384. @brief The button title for automated Facebook sign-in operation.
  385. */
  386. static NSString *const kAutoSignInFacebook = @"Sign In With Facebook";
  387. /** @var kAutoSignUpEmailPassword
  388. @brief The button title for automated sign-up with email/password.
  389. */
  390. static NSString *const kAutoSignUpEmailPassword = @"Sign Up With Email/Password";
  391. /** @var kAutoSignInAnonymously
  392. @brief The button title for automated sign-in anonymously.
  393. */
  394. static NSString *const kAutoSignInAnonymously = @"Sign In Anonymously";
  395. /** @var kAutoAccountLinking
  396. @brief The button title for automated account linking.
  397. */
  398. static NSString *const kAutoAccountLinking = @"Link with Google";
  399. /** @var kAutoPhoneNumberSignIn
  400. @brief The button title for automated Phone Number sign-in.
  401. */
  402. static NSString *const kAutoPhoneNumberSignIn = @"Sign in With Phone Number";
  403. /** @var kGitHubSignInButtonText
  404. @brief The button title for signing in with github.
  405. */
  406. static NSString *const kGitHubSignInButtonText = @"Sign In with GitHub";
  407. /** @var kExpiredCustomTokenUrl
  408. @brief The url for obtaining a valid custom token string used to test BYOAuth.
  409. */
  410. static NSString *const kCustomTokenUrl = @"https://fb-sa-1211.appspot.com/token";
  411. /** @var kCustomTokenUrl
  412. @brief The url for obtaining an expired custom token string used to test BYOAuth.
  413. */
  414. static NSString *const kExpiredCustomTokenUrl = @"https://fb-sa-1211.appspot.com/expired_token";
  415. /** @var kFakeDisplayPhotoUrl
  416. @brief The url for obtaining a display displayPhoto used for testing.
  417. */
  418. static NSString *const kFakeDisplayPhotoUrl =
  419. @"https://www.gstatic.com/images/branding/product/1x/play_apps_48dp.png";
  420. /** @var kFakeDisplayName
  421. @brief Fake display name for testing.
  422. */
  423. static NSString *const kFakeDisplayName = @"John GoogleSpeed";
  424. /** @var kFakeEmail
  425. @brief Fake email for testing.
  426. */
  427. static NSString *const kFakeEmail =@"firemail@example.com";
  428. /** @var kFakePassword
  429. @brief Fake password for testing.
  430. */
  431. static NSString *const kFakePassword =@"fakePassword";
  432. /** @var kInvalidCustomToken
  433. @brief The custom token string for testing BYOAuth.
  434. */
  435. static NSString *const kInvalidCustomToken = @"invalid custom token.";
  436. /** @var kSafariGoogleSignOutMessagePrompt
  437. @brief The message text informing user to sign-out from Google on safari before continuing.
  438. */
  439. static NSString *const kSafariGoogleSignOutMessagePrompt = @"This automated test assumes that no "
  440. "Google account is signed in on Safari, if your are not prompted for a password, sign out on "
  441. "Safari and rerun the test.";
  442. /** @var kSafariFacebookSignOutMessagePrompt
  443. @brief The message text informing user to sign-out from Facebook on safari before continuing.
  444. */
  445. static NSString *const kSafariFacebookSignOutMessagePrompt = @"This automated test assumes that no "
  446. "Facebook account is signed in on Safari, if your are not prompted for a password, sign out on "
  447. "Safari and rerun the test.";
  448. /** @var kUnlinkAccountMessagePrompt
  449. @brief The message text informing user to use an unlinked account for account linking.
  450. */
  451. static NSString *const kUnlinkAccountMessagePrompt = @"Sign into gmail with an email address "
  452. "that has not been linked to this sample application before. Delete account if necessary.";
  453. /** @var kPasswordResetAction
  454. @brief The value for password reset mode in the action code URL.
  455. */
  456. static NSString *const kPasswordResetAction = @"resetPassword";
  457. /** @var kVerifyEmailAction
  458. @brief The value for verify email mode in the action code URL.
  459. */
  460. static NSString *const kVerifyEmailAction = @"verifyEmail";
  461. // Declared extern in .h file.
  462. NSString *const kCreateUserAccessibilityID = @"CreateUserAccessibilityID";
  463. /** @var kPhoneAuthSectionTitle
  464. @brief The title for the phone auth section of the test app.
  465. */
  466. static NSString *const kPhoneAuthSectionTitle = @"Phone Auth";
  467. /** @var kPhoneNumberSignInTitle
  468. @brief The title for button to sign in with phone number.
  469. */
  470. static NSString *const kPhoneNumberSignInTitle = @"Sign in With Phone Number";
  471. /** @var kPhoneNumberSignInTitle
  472. @brief The title for button to sign in with phone number using reCAPTCHA.
  473. */
  474. static NSString *const kPhoneNumberSignInReCaptchaTitle = @"Sign in With Phone Number (reCAPTCHA)";
  475. /** @var kIsNewUserToggleTitle
  476. @brief The title for button to enable new or existing user toggle.
  477. */
  478. static NSString *const kNewOrExistingUserToggleTitle = @"New or Existing User Toggle";
  479. /** @typedef showEmailPasswordDialogCompletion
  480. @brief The type of block which gets called to complete the Email/Password dialog flow.
  481. */
  482. typedef void (^ShowEmailPasswordDialogCompletion)(FIRAuthCredential *credential);
  483. /** @typedef FIRTokenCallback
  484. @brief The type of block which gets called when a token is ready.
  485. */
  486. typedef void (^FIRTokenCallback)(NSString *_Nullable token, NSError *_Nullable error);
  487. /** @brief The request type for OOB action codes.
  488. */
  489. typedef enum {
  490. /** No action code settings. */
  491. ActionCodeRequestTypeEmail,
  492. /** With continue URL but not handled in-app. */
  493. ActionCodeRequestTypeContinue,
  494. /** Handled in-app. */
  495. ActionCodeRequestTypeInApp,
  496. } ActionCodeRequestType;
  497. /** @category FIRAppAssociationRegistration(Deregistration)
  498. @brief The category for the deregistration method.
  499. */
  500. @interface FIRAppAssociationRegistration (Deregistration)
  501. /** @fn deregisteredObjectWithHost:key:
  502. @brief Removes the object that was registered with a particular host and key, if one exists.
  503. @param host The host object.
  504. @param key The key to specify the registered object on the host.
  505. */
  506. + (void)deregisterObjectWithHost:(id)host key:(NSString *)key;
  507. @end
  508. @implementation FIRAppAssociationRegistration (Deregistration)
  509. + (void)deregisterObjectWithHost:(id)host key:(NSString *)key {
  510. @synchronized(self) {
  511. SEL dictKey = @selector(registeredObjectWithHost:key:creationBlock:);
  512. NSMutableDictionary<NSString *, id> *objectsByKey = objc_getAssociatedObject(host, dictKey);
  513. [objectsByKey removeObjectForKey:key];
  514. }
  515. }
  516. @end
  517. @implementation MainViewController {
  518. NSMutableString *_consoleString;
  519. /** @var _authStateDidChangeListeners
  520. @brief An array of handles created during calls to @c FIRAuth.addAuthStateDidChangeListener:
  521. */
  522. NSMutableArray<FIRAuthStateDidChangeListenerHandle> *_authStateDidChangeListeners;
  523. /** @var _IDTokenDidChangeListeners
  524. @brief An array of handles created during calls to @c FIRAuth.addIDTokenDidChangeListener:
  525. */
  526. NSMutableArray<FIRAuthStateDidChangeListenerHandle> *_IDTokenDidChangeListeners;
  527. /** @var _userInMemory
  528. @brief Acts like the "memory" function of a calculator. An operation allows sample app users
  529. to assign this value based on @c FIRAuth.currentUser or clear this value.
  530. */
  531. FIRUser *_userInMemory;
  532. /** @var _useUserInMemory
  533. @brief Instructs the application to use _userInMemory instead of @c FIRAuth.currentUser for
  534. testing operations. This allows us to test if things still work with a user who is not
  535. the @c FIRAuth.currentUser, and also allows us to test those things while
  536. @c FIRAuth.currentUser remains nil (after a sign-out) and also when @c FIRAuth.currentUser
  537. is non-nil (do to a subsequent sign-in.)
  538. */
  539. BOOL _useUserInMemory;
  540. /** @var _newUserToggle
  541. @brief When enabled allows login popup alert determining new or existing user.
  542. */
  543. BOOL _isNewUserToggleOn;
  544. /** @var _actionCodeRequestType
  545. @brief The type for the next action code request.
  546. */
  547. ActionCodeRequestType _actionCodeRequestType;
  548. /** @var _actionCodeContinueURL
  549. @brief The continue URL to be used in the next action code request.
  550. */
  551. NSURL *_actionCodeContinueURL;
  552. }
  553. /** @fn initWithNibName:bundle:
  554. @brief Overridden default initializer.
  555. */
  556. - (id)initWithNibName:(nullable NSString *)nibNameOrNil bundle:(nullable NSBundle *)nibBundleOrNil {
  557. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  558. if (self) {
  559. _actionCodeRequestType = ActionCodeRequestTypeInApp;
  560. _actionCodeContinueURL = [NSURL URLWithString:KCONTINUE_URL];
  561. _authStateDidChangeListeners = [NSMutableArray array];
  562. _IDTokenDidChangeListeners = [NSMutableArray array];
  563. [[NSNotificationCenter defaultCenter] addObserver:self
  564. selector:@selector(authStateChangedForAuth:)
  565. name:FIRAuthStateDidChangeNotification
  566. object:nil];
  567. self.useStatusBarSpinner = YES;
  568. }
  569. return self;
  570. }
  571. - (void)viewDidLoad {
  572. // Give us a circle for the image view:
  573. _userInfoTableViewCell.userInfoProfileURLImageView.layer.cornerRadius =
  574. _userInfoTableViewCell.userInfoProfileURLImageView.frame.size.width / 2.0f;
  575. _userInfoTableViewCell.userInfoProfileURLImageView.layer.masksToBounds = YES;
  576. _userInMemoryInfoTableViewCell.userInfoProfileURLImageView.layer.cornerRadius =
  577. _userInMemoryInfoTableViewCell.userInfoProfileURLImageView.frame.size.width / 2.0f;
  578. _userInMemoryInfoTableViewCell.userInfoProfileURLImageView.layer.masksToBounds = YES;
  579. }
  580. - (void)viewWillAppear:(BOOL)animated {
  581. [super viewWillAppear:animated];
  582. [self updateTable];
  583. [self updateUserInfo];
  584. }
  585. - (void)updateTable {
  586. __weak typeof(self) weakSelf = self;
  587. _tableViewManager.contents =
  588. [StaticContentTableViewContent contentWithSections:@[
  589. [StaticContentTableViewSection sectionWithTitle:kSectionTitleUserDetails cells:@[
  590. [StaticContentTableViewCell cellWithCustomCell:_userInfoTableViewCell action:^{
  591. [weakSelf presentUserInfo];
  592. }],
  593. [StaticContentTableViewCell cellWithCustomCell:_userToUseCell],
  594. [StaticContentTableViewCell cellWithCustomCell:_userInMemoryInfoTableViewCell action:^{
  595. [weakSelf presentUserInMemoryInfo];
  596. }],
  597. ]],
  598. [StaticContentTableViewSection sectionWithTitle:kSectionTitleSettings cells:@[
  599. [StaticContentTableViewCell cellWithTitle:NSLocalizedString(@"SETTINGSKEY",
  600. kSettingsButtonTextDesription)
  601. action:^{ [weakSelf presentSettings]; }],
  602. [StaticContentTableViewCell cellWithTitle:kNewOrExistingUserToggleTitle
  603. value:_isNewUserToggleOn ? @"Enabled" : @"Disabled"
  604. action:^{
  605. _isNewUserToggleOn = !_isNewUserToggleOn;
  606. [self updateTable]; }],
  607. ]],
  608. [StaticContentTableViewSection sectionWithTitle:kPhoneAuthSectionTitle cells:@[
  609. [StaticContentTableViewCell cellWithTitle:kPhoneNumberSignInReCaptchaTitle
  610. action:^{ [weakSelf signInWithPhoneNumberWithPrompt]; }],
  611. [StaticContentTableViewCell cellWithTitle:kPhoneNumberSignInTitle
  612. action:^{ [weakSelf signInWithPhoneNumber]; }],
  613. [StaticContentTableViewCell cellWithTitle:kUpdatePhoneNumber
  614. action:^{ [weakSelf updatePhoneNumberWithPrompt]; }],
  615. [StaticContentTableViewCell cellWithTitle:kLinkPhoneNumber
  616. action:^{ [weakSelf linkPhoneNumberWithPrompt]; }],
  617. [StaticContentTableViewCell cellWithTitle:kUnlinkPhoneNumber
  618. action:^{
  619. [weakSelf unlinkFromProvider:FIRPhoneAuthProviderID completion:nil];
  620. }],
  621. ]],
  622. [StaticContentTableViewSection sectionWithTitle:kSectionTitleSignIn cells:@[
  623. [StaticContentTableViewCell cellWithTitle:kUpdateCurrentUserFromSavedTitle
  624. value:nil
  625. action:^{ [weakSelf updateToSavedUser]; }],
  626. [StaticContentTableViewCell cellWithTitle:kCreateUserTitle
  627. value:nil
  628. action:^{ [weakSelf createUser]; }
  629. accessibilityID:kCreateUserAccessibilityID],
  630. [StaticContentTableViewCell cellWithTitle:kCreateUserAuthDataResultTitle
  631. action:^{ [weakSelf createUserAuthDataResult]; }],
  632. [StaticContentTableViewCell cellWithTitle:kSignInGoogleButtonText
  633. action:^{ [weakSelf signInGoogle]; }],
  634. [StaticContentTableViewCell cellWithTitle:kSignInWithEmailLink
  635. action:^{ [weakSelf signInWithEmailLink]; }],
  636. [StaticContentTableViewCell cellWithTitle:kVerifyEmailLinkAccount
  637. action:^{ [weakSelf verifyEmailLinkAccount]; }],
  638. [StaticContentTableViewCell cellWithTitle:kSendEmailSignInLink
  639. action:^{ [weakSelf sendEmailSignInLink]; }],
  640. [StaticContentTableViewCell cellWithTitle:kSignInGoogleAndRetrieveDataButtonText
  641. action:^{ [weakSelf signInGoogleAndRetrieveData]; }],
  642. [StaticContentTableViewCell cellWithTitle:kSignInFacebookButtonText
  643. action:^{ [weakSelf signInFacebook]; }],
  644. [StaticContentTableViewCell cellWithTitle:kSignInFacebookAndRetrieveDataButtonText
  645. action:^{ [weakSelf signInFacebookAndRetrieveData]; }],
  646. [StaticContentTableViewCell cellWithTitle:kSignInEmailPasswordButtonText
  647. action:^{ [weakSelf signInEmailPassword]; }],
  648. [StaticContentTableViewCell cellWithTitle:kSignInEmailPasswordAuthDataResultButtonText
  649. action:^{ [weakSelf signInEmailPasswordAuthDataResult]; }],
  650. [StaticContentTableViewCell cellWithTitle:kSignInWithCustomTokenButtonText
  651. action:^{ [weakSelf signInWithCustomToken]; }],
  652. [StaticContentTableViewCell cellWithTitle:kSignInWithCustomAuthResultTokenButtonText
  653. action:^{ [weakSelf signInWithCustomTokenAuthResult]; }],
  654. [StaticContentTableViewCell cellWithTitle:kSignInAnonymouslyButtonText
  655. action:^{ [weakSelf signInAnonymously]; }],
  656. [StaticContentTableViewCell cellWithTitle:kSignInAnonymouslyWithAuthResultButtonText
  657. action:^{ [weakSelf signInAnonymouslyAuthDataResult]; }],
  658. [StaticContentTableViewCell cellWithTitle:kGitHubSignInButtonText
  659. action:^{ [weakSelf signInWithGitHub]; }],
  660. [StaticContentTableViewCell cellWithTitle:kSignOutButtonText
  661. action:^{ [weakSelf signOut]; }]
  662. ]],
  663. [StaticContentTableViewSection sectionWithTitle:kSectionTitleUserActions cells:@[
  664. [StaticContentTableViewCell cellWithTitle:kSetDisplayNameTitle
  665. action:^{ [weakSelf setDisplayName]; }],
  666. [StaticContentTableViewCell cellWithTitle:kSetPhotoURLText
  667. action:^{ [weakSelf setPhotoURL]; }],
  668. [StaticContentTableViewCell cellWithTitle:kReloadText
  669. action:^{ [weakSelf reloadUser]; }],
  670. [StaticContentTableViewCell cellWithTitle:kGetProvidersForEmail
  671. action:^{ [weakSelf getProvidersForEmail]; }],
  672. [StaticContentTableViewCell cellWithTitle:kGetAllSignInMethodsForEmail
  673. action:^{ [weakSelf getAllSignInMethodsForEmail]; }],
  674. [StaticContentTableViewCell cellWithTitle:kUpdateEmailText
  675. action:^{ [weakSelf updateEmail]; }],
  676. [StaticContentTableViewCell cellWithTitle:kUpdatePasswordText
  677. action:^{ [weakSelf updatePassword]; }],
  678. [StaticContentTableViewCell cellWithTitle:kDeleteUserText
  679. action:^{ [weakSelf deleteAccount]; }],
  680. ]],
  681. [StaticContentTableViewSection sectionWithTitle:kSectionTitleOOBActions cells:@[
  682. [StaticContentTableViewCell cellWithTitle:kActionCodeTypeDescription
  683. value:[self actionCodeRequestTypeString]
  684. action:^{ [weakSelf toggleActionCodeRequestType]; }],
  685. [StaticContentTableViewCell cellWithTitle:kContinueURLDescription
  686. value:_actionCodeContinueURL.absoluteString ?: @"(nil)"
  687. action:^{ [weakSelf changeActionCodeContinueURL]; }],
  688. [StaticContentTableViewCell cellWithTitle:kRequestVerifyEmail
  689. action:^{ [weakSelf requestVerifyEmail]; }],
  690. [StaticContentTableViewCell cellWithTitle:kRequestPasswordReset
  691. action:^{ [weakSelf requestPasswordReset]; }],
  692. [StaticContentTableViewCell cellWithTitle:kResetPassword
  693. action:^{ [weakSelf resetPassword]; }],
  694. [StaticContentTableViewCell cellWithTitle:kCheckActionCode
  695. action:^{ [weakSelf checkActionCode]; }],
  696. [StaticContentTableViewCell cellWithTitle:kApplyActionCode
  697. action:^{ [weakSelf applyActionCode]; }],
  698. [StaticContentTableViewCell cellWithTitle:kVerifyPasswordResetCode
  699. action:^{ [weakSelf verifyPasswordResetCode]; }],
  700. ]],
  701. [StaticContentTableViewSection sectionWithTitle:kSectionTitleReauthenticate cells:@[
  702. [StaticContentTableViewCell cellWithTitle:kReauthenticateGoogleText
  703. action:^{ [weakSelf reauthenticateGoogle]; }],
  704. [StaticContentTableViewCell
  705. cellWithTitle:kReauthenticateGoogleAndRetrieveDataText
  706. action:^{ [weakSelf reauthenticateGoogleAndRetrieveData]; }],
  707. [StaticContentTableViewCell cellWithTitle:kReauthenticateFBText
  708. action:^{ [weakSelf reauthenticateFB]; }],
  709. [StaticContentTableViewCell cellWithTitle:kReauthenticateFBAndRetrieveDataText
  710. action:^{ [weakSelf reauthenticateFBAndRetrieveData]; }],
  711. [StaticContentTableViewCell cellWithTitle:kReauthenticateEmailText
  712. action:^{ [weakSelf reauthenticateEmailPassword]; }]
  713. ]],
  714. [StaticContentTableViewSection sectionWithTitle:kSectionTitleTokenActions cells:@[
  715. [StaticContentTableViewCell cellWithTitle:kTokenGetButtonText
  716. action:^{ [weakSelf getUserTokenWithForce:NO]; }],
  717. [StaticContentTableViewCell cellWithTitle:kTokenRefreshButtonText
  718. action:^{ [weakSelf getUserTokenWithForce:YES]; }],
  719. [StaticContentTableViewCell cellWithTitle:kGetTokenResultButtonText
  720. action:^{ [weakSelf getUserTokenResultWithForce:NO]; }],
  721. [StaticContentTableViewCell cellWithTitle:kGetTokenResultForceButtonText
  722. action:^{ [weakSelf getUserTokenResultWithForce:YES]; }],
  723. ]],
  724. [StaticContentTableViewSection sectionWithTitle:kSectionTitleLinkUnlinkAccounts cells:@[
  725. [StaticContentTableViewCell cellWithTitle:kLinkWithGoogleText
  726. action:^{ [weakSelf linkWithGoogle]; }],
  727. [StaticContentTableViewCell cellWithTitle:kLinkWithGoogleAndRetrieveDataText
  728. action:^{ [weakSelf linkWithGoogleAndRetrieveData]; }],
  729. [StaticContentTableViewCell cellWithTitle:kLinkWithFacebookText
  730. action:^{ [weakSelf linkWithFacebook]; }],
  731. [StaticContentTableViewCell cellWithTitle:kLinkWithFacebookAndRetrieveDataText
  732. action:^{ [weakSelf linkWithFacebookAndRetrieveData]; }],
  733. [StaticContentTableViewCell cellWithTitle:kLinkWithEmailPasswordText
  734. action:^{ [weakSelf linkWithEmailPassword]; }],
  735. [StaticContentTableViewCell cellWithTitle:kUnlinkFromGoogle
  736. action:^{
  737. [weakSelf unlinkFromProvider:FIRGoogleAuthProviderID completion:nil];
  738. }],
  739. [StaticContentTableViewCell cellWithTitle:kUnlinkFromFacebook
  740. action:^{
  741. [weakSelf unlinkFromProvider:FIRGoogleAuthProviderID completion:nil];
  742. }],
  743. [StaticContentTableViewCell cellWithTitle:kUnlinkFromEmailPassword
  744. action:^{
  745. [weakSelf unlinkFromProvider:FIRGoogleAuthProviderID completion:nil];
  746. }]
  747. ]],
  748. [StaticContentTableViewSection sectionWithTitle:kSectionTitleApp cells:@[
  749. [StaticContentTableViewCell cellWithTitle:kDeleteAppTitle
  750. action:^{ [weakSelf deleteApp]; }],
  751. [StaticContentTableViewCell cellWithTitle:kTokenGetButtonText
  752. action:^{ [weakSelf getAppTokenWithForce:NO]; }],
  753. [StaticContentTableViewCell cellWithTitle:kTokenRefreshButtonText
  754. action:^{ [weakSelf getAppTokenWithForce:YES]; }],
  755. [StaticContentTableViewCell cellWithTitle:kTimeAuthInitTitle
  756. action:^{ [weakSelf timeAuthInitialization]; }]
  757. ]],
  758. [StaticContentTableViewSection sectionWithTitle:kSectionTitleListeners cells:@[
  759. [StaticContentTableViewCell cellWithTitle:kAddAuthStateListenerTitle
  760. action:^{ [weakSelf addAuthStateListener]; }],
  761. [StaticContentTableViewCell cellWithTitle:kRemoveAuthStateListenerTitle
  762. action:^{ [weakSelf removeAuthStateListener]; }],
  763. [StaticContentTableViewCell cellWithTitle:kAddIDTokenListenerTitle
  764. action:^{ [weakSelf addIDTokenListener]; }],
  765. [StaticContentTableViewCell cellWithTitle:kRemoveIDTokenListenerTitle
  766. action:^{ [weakSelf removeIDTokenListener]; }],
  767. ]],
  768. [StaticContentTableViewSection sectionWithTitle:kSectionTitleManualTests cells:@[
  769. [StaticContentTableViewCell cellWithTitle:kAutoBYOAuthTitle
  770. action:^{ [weakSelf automatedBYOAuth]; }],
  771. [StaticContentTableViewCell cellWithTitle:kAutoSignInGoogle
  772. action:^{ [weakSelf automatedSignInGoogle]; }],
  773. [StaticContentTableViewCell cellWithTitle:kAutoSignInFacebook
  774. action:^{ [weakSelf automatedSignInFacebook]; }],
  775. [StaticContentTableViewCell cellWithTitle:kAutoSignUpEmailPassword
  776. action:^{ [weakSelf automatedEmailSignUp]; }],
  777. [StaticContentTableViewCell cellWithTitle:kAutoSignInAnonymously
  778. action:^{ [weakSelf automatedAnonymousSignIn]; }],
  779. [StaticContentTableViewCell cellWithTitle:kAutoAccountLinking
  780. action:^{ [weakSelf automatedAccountLinking]; }],
  781. [StaticContentTableViewCell cellWithTitle:kAutoPhoneNumberSignIn
  782. action:^{ [weakSelf automatedPhoneNumberSignIn]; }]
  783. ]]
  784. ]];
  785. }
  786. #pragma mark - Interface Builder Actions
  787. - (IBAction)userToUseDidChange:(UISegmentedControl *)sender {
  788. _useUserInMemory = (sender.selectedSegmentIndex == 1);
  789. }
  790. - (IBAction)memoryPlus {
  791. _userInMemory = [AppManager auth].currentUser;
  792. [self updateUserInfo];
  793. }
  794. - (IBAction)memoryClear {
  795. _userInMemory = nil;
  796. [self updateUserInfo];
  797. }
  798. /** @fn parseURL
  799. @brief Parses an incoming URL into all available query items.
  800. @param urlString The url to be parsed.
  801. @return A dictionary of available query items in the target URL.
  802. */
  803. static NSDictionary<NSString *, NSString *> *parseURL(NSString *urlString) {
  804. NSString *linkURL = [NSURLComponents componentsWithString:urlString].query;
  805. NSArray<NSString *> *URLComponents = [linkURL componentsSeparatedByString:@"&"];
  806. NSMutableDictionary<NSString *, NSString *> *queryItems =
  807. [[NSMutableDictionary alloc] initWithCapacity:URLComponents.count];
  808. for (NSString *component in URLComponents) {
  809. NSRange equalRange = [component rangeOfString:@"="];
  810. if (equalRange.location != NSNotFound) {
  811. NSString *queryItemKey =
  812. [[component substringToIndex:equalRange.location] stringByRemovingPercentEncoding];
  813. NSString *queryItemValue =
  814. [[component substringFromIndex:equalRange.location + 1] stringByRemovingPercentEncoding];
  815. if (queryItemKey && queryItemValue) {
  816. queryItems[queryItemKey] = queryItemValue;
  817. }
  818. }
  819. }
  820. return queryItems;
  821. }
  822. #pragma mark public methods
  823. - (BOOL)handleIncomingLinkWithURL:(NSURL *)URL {
  824. // Parse the query portion of the incoming URL.
  825. NSDictionary<NSString *, NSString *> *queryItems =
  826. parseURL([NSURLComponents componentsWithString:URL.absoluteString].query);
  827. // Check that all necessary query items are available.
  828. NSString *actionCode = queryItems[@"oobCode"];
  829. NSString *mode = queryItems[@"mode"];
  830. if (!actionCode || !mode) {
  831. return NO;
  832. }
  833. // Handle Password Reset action.
  834. if ([mode isEqualToString:kPasswordResetAction]) {
  835. [self showTextInputPromptWithMessage:@"New Password:"
  836. completionBlock:^(BOOL userPressedOK, NSString *_Nullable newPassword) {
  837. if (!userPressedOK || !newPassword.length) {
  838. [UIPasteboard generalPasteboard].string = actionCode;
  839. return;
  840. }
  841. [self showSpinner:^() {
  842. [[AppManager auth] confirmPasswordResetWithCode:actionCode
  843. newPassword:newPassword
  844. completion:^(NSError *_Nullable error) {
  845. [self hideSpinner:^{
  846. if (error) {
  847. [self logFailure:@"Password reset in app failed" error:error];
  848. [self showMessagePrompt:error.localizedDescription];
  849. return;
  850. }
  851. [self logSuccess:@"Password reset in app succeeded."];
  852. [self showMessagePrompt:@"Password reset in app succeeded."];
  853. }];
  854. }];
  855. }];
  856. }];
  857. return YES;
  858. }
  859. if ([mode isEqualToString:kVerifyEmailAction]) {
  860. [self showMessagePromptWithTitle:@"Tap OK to verify email"
  861. message:actionCode
  862. showCancelButton:YES
  863. completion:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  864. if (!userPressedOK) {
  865. return;
  866. }
  867. [self showSpinner:^() {
  868. [[AppManager auth] applyActionCode:actionCode completion:^(NSError *_Nullable error) {
  869. [self hideSpinner:^{
  870. if (error) {
  871. [self logFailure:@"Verify email in app failed" error:error];
  872. [self showMessagePrompt:error.localizedDescription];
  873. return;
  874. }
  875. [self logSuccess:@"Verify email in app succeeded."];
  876. [self showMessagePrompt:@"Verify email in app succeeded."];
  877. }];
  878. }];
  879. }];
  880. }];
  881. return YES;
  882. }
  883. return NO;
  884. }
  885. #pragma mark - Actions
  886. /** @fn signInWithProvider:provider:
  887. @brief Perform sign in with credential operataion, for given auth provider.
  888. @param provider The auth provider.
  889. @param callback The callback to continue the flow which executed this sign-in.
  890. */
  891. - (void)signInWithProvider:(nonnull id<AuthProvider>)provider callback:(void(^)(void))callback {
  892. if (!provider) {
  893. [self logFailedTest:@"A valid auth provider was not provided to the signInWithProvider."];
  894. return;
  895. }
  896. [provider getAuthCredentialWithPresentingViewController:self
  897. callback:^(FIRAuthCredential *credential,
  898. NSError *error) {
  899. if (!credential) {
  900. [self logFailedTest:@"The test needs a valid credential to continue."];
  901. return;
  902. }
  903. [[AppManager auth] signInWithCredential:credential completion:^(FIRUser *_Nullable user,
  904. NSError *_Nullable error) {
  905. if (error) {
  906. [self logFailure:@"sign-in with provider failed" error:error];
  907. [self logFailedTest:@"Sign-in should succeed"];
  908. return;
  909. } else {
  910. [self logSuccess:@"sign-in with provider succeeded."];
  911. callback();
  912. }
  913. }];
  914. }];
  915. }
  916. /** @fn automatedSignInGoogle
  917. @brief Automatically executes the manual test for sign-in with Google.
  918. */
  919. - (void)automatedSignInGoogle {
  920. [self showMessagePromptWithTitle:kAutoSignInGoogle
  921. message:kSafariGoogleSignOutMessagePrompt
  922. showCancelButton:NO
  923. completion:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  924. FIRAuth *auth = [AppManager auth];
  925. if (!auth) {
  926. [self logFailedTest:@"Could not obtain auth object."];
  927. return;
  928. }
  929. [auth signOut:NULL];
  930. [self log:@"INITIATING AUTOMATED MANUAL TEST FOR GOOGLE SIGN IN:"];
  931. [self signInWithProvider:[AuthProviders google] callback:^{
  932. [self logSuccess:@"sign-in with Google provider succeeded."];
  933. [auth signOut:NULL];
  934. [self signInWithProvider:[AuthProviders google] callback:^{
  935. [self logSuccess:@"sign-in with Google provider succeeded."];
  936. [self updateEmailPasswordWithCompletion:^{
  937. [self automatedSignInGoogleDisplayNamePhotoURL];
  938. }];
  939. }];
  940. }];
  941. }];
  942. }
  943. /** @fn automatedSignInGoogleDisplayNamePhotoURL
  944. @brief Automatically executes the manual test for setting email and password for sign in with
  945. Google.
  946. */
  947. - (void)automatedSignInGoogleDisplayNamePhotoURL {
  948. [self signInWithProvider:[AuthProviders google] callback:^{
  949. [self updateDisplayNameAndPhotoURlWithCompletion:^{
  950. [self log:@"FINISHED AUTOMATED MANUAL TEST FOR SIGN-IN WITH GOOGlE."];
  951. [self reloadUser];
  952. }];
  953. }];
  954. }
  955. /** @fn automatedSignInFacebook
  956. @brief Automatically executes the manual test for sign-in with Facebook.
  957. */
  958. - (void)automatedSignInFacebook {
  959. [self showMessagePromptWithTitle:kAutoSignInFacebook
  960. message:kSafariFacebookSignOutMessagePrompt
  961. showCancelButton:NO
  962. completion:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  963. FIRAuth *auth = [AppManager auth];
  964. if (!auth) {
  965. [self logFailedTest:@"Could not obtain auth object."];
  966. return;
  967. }
  968. [auth signOut:NULL];
  969. [self log:@"INITIATING AUTOMATED MANUAL TEST FOR FACEBOOK SIGN IN:"];
  970. [self signInWithProvider:[AuthProviders facebook] callback:^{
  971. [self logSuccess:@"sign-in with Facebook provider succeeded."];
  972. [auth signOut:NULL];
  973. [self signInWithProvider:[AuthProviders facebook] callback:^{
  974. [self logSuccess:@"sign-in with Facebook provider succeeded."];
  975. [self updateEmailPasswordWithCompletion:^{
  976. [self automatedSignInFacebookDisplayNamePhotoURL];
  977. }];
  978. }];
  979. }];
  980. }];
  981. }
  982. /** @fn automatedPhoneNumberSignIn
  983. @brief Automatically executes the manual test for sign-in with phone number.
  984. */
  985. - (void)automatedPhoneNumberSignIn {
  986. [self log:@"Automated phone number sign in"];
  987. FIRAuth *auth = [AppManager auth];
  988. if (!auth) {
  989. [self logFailedTest:@"Could not obtain auth object."];
  990. return;
  991. }
  992. [auth signOut:NULL];
  993. [self log:@"INITIATING AUTOMATED MANUAL TEST FOR PHONE NUMBER SIGN IN:"];
  994. [self commonPhoneNumberInputWithTitle:@"Phone for automation"
  995. Completion:^(NSString *_Nullable phone) {
  996. [self signInWithPhoneNumber:phone completion:^(NSError *error) {
  997. if (error) {
  998. [self logFailedTest:@"Could not sign in with phone number reCAPTCHA."];
  999. }
  1000. [self logSuccess:@"sign-in with phone number reCAPTCHA test succeeded."];
  1001. [auth signOut:NULL];
  1002. [self signInWithPhoneNumber:phone completion:^(NSError *error) {
  1003. if (error) {
  1004. [self logFailedTest:@"Could not sign in with phone number reCAPTCHA."];
  1005. }
  1006. [self logSuccess:@"second sign-in with phone number reCAPTCHA test succeeded."];
  1007. [self updatePhoneNumber:phone completion:^(NSError *error) {
  1008. if (error) {
  1009. [self logFailedTest:@"Could not update phone number."];
  1010. }
  1011. [self logSuccess:@"update phone number test succeeded."];
  1012. [self unlinkFromProvider:FIRPhoneAuthProviderID completion:^(NSError *error) {
  1013. if (error) {
  1014. [self logFailedTest:@"Could not unlink phone number."];
  1015. }
  1016. [self logSuccess:@"unlink phone number test succeeded."];
  1017. [self linkPhoneNumber:phone completion:^(NSError *error) {
  1018. if (error) {
  1019. [self logFailedTest:@"Could not link phone number."];
  1020. }
  1021. [self logSuccess:@"link phone number test succeeded."];
  1022. [self log:@"FINISHED AUTOMATED MANUAL TEST FOR PHONE NUMBER SIGN IN."];
  1023. }];
  1024. }];
  1025. }];
  1026. }];
  1027. }];
  1028. }];
  1029. }
  1030. /** @fn automatedEmailSignUp
  1031. @brief Automatically executes the manual test for sign-up with email/password.
  1032. */
  1033. - (void)automatedEmailSignUp {
  1034. [self log:@"INITIATING AUTOMATED MANUAL TEST FOR FACEBOOK SIGN IN:"];
  1035. FIRAuth *auth = [AppManager auth];
  1036. if (!auth) {
  1037. [self logFailedTest:@"Could not obtain auth object."];
  1038. return;
  1039. }
  1040. [self signUpNewEmail:kFakeEmail password:kFakePassword callback:^(FIRUser *_Nullable user,
  1041. NSError *_Nullable error) {
  1042. if (error) {
  1043. [self logFailedTest: @" Email/Password Account account creation failed"];
  1044. return;
  1045. }
  1046. [auth signOut:NULL];
  1047. FIRAuthCredential *credential = [FIREmailAuthProvider credentialWithEmail:kFakeEmail
  1048. password:kFakePassword];
  1049. [auth signInWithCredential:credential
  1050. completion:^(FIRUser *_Nullable user,
  1051. NSError *_Nullable error) {
  1052. if (error) {
  1053. [self logFailure:@"sign-in with Email/Password failed" error:error];
  1054. [self logFailedTest:@"sign-in with Email/Password should succeed."];
  1055. return;
  1056. }
  1057. [self logSuccess:@"sign-in with Email/Password succeeded."];
  1058. [self log:@"FINISHED AUTOMATED MANUAL TEST FOR SIGN-IN WITH EMAIL/PASSWORD."];
  1059. // Delete the user so that we can reuse the fake email address for subsequent tests.
  1060. [auth.currentUser deleteWithCompletion:^(NSError *_Nullable error) {
  1061. if (error) {
  1062. [self logFailure:@"Failed to delete user" error:error];
  1063. [self logFailedTest:@"Deleting a user that was recently signed-in should succeed."];
  1064. return;
  1065. }
  1066. [self logSuccess:@"User deleted."];
  1067. }];
  1068. }];
  1069. }];
  1070. }
  1071. /** @fn automatedAnonymousSignIn
  1072. @brief Automatically executes the manual test for sign-in anonymously.
  1073. */
  1074. - (void)automatedAnonymousSignIn {
  1075. [self log:@"INITIATING AUTOMATED MANUAL TEST FOR ANONYMOUS SIGN IN:"];
  1076. FIRAuth *auth = [AppManager auth];
  1077. if (!auth) {
  1078. [self logFailedTest:@"Could not obtain auth object."];
  1079. return;
  1080. }
  1081. [auth signOut:NULL];
  1082. [self signInAnonymouslyWithCallback:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  1083. if (user) {
  1084. NSString *anonymousUID = user.uid;
  1085. [self signInAnonymouslyWithCallback:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  1086. if (![user.uid isEqual:anonymousUID]) {
  1087. [self logFailedTest:@"Consecutive anonymous sign-ins should yeild the same User ID"];
  1088. return;
  1089. }
  1090. [self log:@"FINISHED AUTOMATED MANUAL TEST FOR ANONYMOUS SIGN IN."];
  1091. }];
  1092. }
  1093. }];
  1094. }
  1095. /** @fn signInAnonymouslyWithCallback:
  1096. @brief Performs anonymous sign in and then executes callback.
  1097. @param callback The callback to be executed.
  1098. */
  1099. - (void)signInAnonymouslyWithCallback:(nullable FIRAuthResultCallback)callback {
  1100. FIRAuth *auth = [AppManager auth];
  1101. if (!auth) {
  1102. [self logFailedTest:@"Could not obtain auth object."];
  1103. return;
  1104. }
  1105. [auth signInAnonymouslyWithCompletion:^(FIRUser *_Nullable user,
  1106. NSError *_Nullable error) {
  1107. if (error) {
  1108. [self logFailure:@"sign-in anonymously failed" error:error];
  1109. [self logFailedTest:@"Recently signed out user should be able to sign in anonymously."];
  1110. return;
  1111. }
  1112. [self logSuccess:@"sign-in anonymously succeeded."];
  1113. if (callback) {
  1114. callback(user, nil);
  1115. }
  1116. }];
  1117. }
  1118. /** @fn automatedAccountLinking
  1119. @brief Automatically executes the manual test for account linking.
  1120. */
  1121. - (void)automatedAccountLinking {
  1122. [self log:@"INITIATING AUTOMATED MANUAL TEST FOR ACCOUNT LINKING:"];
  1123. FIRAuth *auth = [AppManager auth];
  1124. if (!auth) {
  1125. [self logFailedTest:@"Could not obtain auth object."];
  1126. return;
  1127. }
  1128. [auth signOut:NULL];
  1129. [self signInAnonymouslyWithCallback:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  1130. if (user) {
  1131. NSString *anonymousUID = user.uid;
  1132. [self showMessagePromptWithTitle:@"Sign In Instructions"
  1133. message:kUnlinkAccountMessagePrompt
  1134. showCancelButton:NO
  1135. completion:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  1136. [[AuthProviders google]
  1137. getAuthCredentialWithPresentingViewController:self
  1138. callback:^(FIRAuthCredential *credential,
  1139. NSError *error) {
  1140. if (credential) {
  1141. [user linkWithCredential:credential completion:^(FIRUser *user, NSError *error) {
  1142. if (error) {
  1143. [self logFailure:@"link auth provider failed" error:error];
  1144. [self logFailedTest:@"Account needs to be linked to complete the test."];
  1145. return;
  1146. }
  1147. [self logSuccess:@"link auth provider succeeded."];
  1148. if (user.isAnonymous) {
  1149. [self logFailure:@"link auth provider failed, user still anonymous" error:error];
  1150. [self logFailedTest:@"Account needs to be linked to complete the test."];
  1151. }
  1152. if (![user.uid isEqual:anonymousUID]){
  1153. [self logFailedTest:@"link auth provider failed, UID's are different. Make sure "
  1154. "you link an account that has NOT been Linked nor Signed-In before."];
  1155. return;
  1156. }
  1157. [self log:@"FINISHED AUTOMATED MANUAL TEST FOR ACCOUNT LINKING."];
  1158. }];
  1159. }
  1160. }];
  1161. }];
  1162. }
  1163. }];
  1164. }
  1165. /** @fn automatedSignInFacebookDisplayNamePhotoURL
  1166. @brief Automatically executes the manual test for setting email and password for sign-in with
  1167. Facebook.
  1168. */
  1169. - (void)automatedSignInFacebookDisplayNamePhotoURL {
  1170. [self signInWithProvider:[AuthProviders facebook] callback:^{
  1171. [self updateDisplayNameAndPhotoURlWithCompletion:^{
  1172. [self log:@"FINISHED AUTOMATED MANUAL TEST FOR SIGN-IN WITH FACEBOOK."];
  1173. [self reloadUser];
  1174. }];
  1175. }];
  1176. }
  1177. /** @fn automatedBYOauth
  1178. @brief Automatically executes the manual test for BYOAuth.
  1179. */
  1180. - (void)automatedBYOAuth {
  1181. [self log:@"INITIATING AUTOMATED MANUAL TEST FOR BYOAUTH:"];
  1182. [self showSpinner:^{
  1183. NSError *error;
  1184. NSString *customToken = [NSString stringWithContentsOfURL:[NSURL URLWithString:kCustomTokenUrl]
  1185. encoding:NSUTF8StringEncoding
  1186. error:&error];
  1187. NSString *expiredCustomToken =
  1188. [NSString stringWithContentsOfURL:[NSURL URLWithString:kExpiredCustomTokenUrl]
  1189. encoding:NSUTF8StringEncoding
  1190. error:&error];
  1191. [self hideSpinner:^{
  1192. if (error) {
  1193. [self log:@"There was an error retrieving the custom token."];
  1194. return;
  1195. }
  1196. FIRAuth *auth = [AppManager auth];
  1197. [auth signInWithCustomToken:customToken
  1198. completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  1199. if (error) {
  1200. [self logFailure:@"sign-in with custom token failed" error:error];
  1201. [self logFailedTest:@"A fresh custom token should succeed in signing-in."];
  1202. return;
  1203. }
  1204. [self logSuccess:@"sign-in with custom token succeeded."];
  1205. [auth.currentUser getIDTokenForcingRefresh:NO
  1206. completion:^(NSString *_Nullable token,
  1207. NSError *_Nullable error) {
  1208. if (error) {
  1209. [self logFailure:@"refresh token failed" error:error];
  1210. [self logFailedTest:@"Refresh token should be available."];
  1211. return;
  1212. }
  1213. [self logSuccess:@"refresh token succeeded."];
  1214. [auth signOut:NULL];
  1215. [auth signInWithCustomToken:expiredCustomToken
  1216. completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  1217. if (!error) {
  1218. [self logSuccess:@"sign-in with custom token succeeded."];
  1219. [self logFailedTest:@"sign-in with an expired custom token should NOT succeed."];
  1220. return;
  1221. }
  1222. [self logFailure:@"sign-in with custom token failed" error:error];
  1223. [auth signInWithCustomToken:kInvalidCustomToken
  1224. completion:^(FIRUser *_Nullable user,
  1225. NSError *_Nullable error) {
  1226. if (!error) {
  1227. [self logSuccess:@"sign-in with custom token succeeded."];
  1228. [self logFailedTest:@"sign-in with an invalid custom token should NOT succeed."];
  1229. return;
  1230. }
  1231. [self logFailure:@"sign-in with custom token failed" error:error];
  1232. //next step of automated test.
  1233. [self automatedBYOAuthEmailPassword];
  1234. }];
  1235. }];
  1236. }];
  1237. }];
  1238. }];
  1239. }];
  1240. }
  1241. /** @fn automatedBYOAuthEmailPassword
  1242. @brief Automatically executes the manual test for setting email and password in BYOAuth.
  1243. */
  1244. - (void)automatedBYOAuthEmailPassword {
  1245. [self showSpinner:^{
  1246. NSError *error;
  1247. NSString *customToken = [NSString stringWithContentsOfURL:[NSURL URLWithString:kCustomTokenUrl]
  1248. encoding:NSUTF8StringEncoding
  1249. error:&error];
  1250. [self hideSpinner:^{
  1251. if (error) {
  1252. [self log:@"There was an error retrieving the custom token."];
  1253. return;
  1254. }
  1255. [[AppManager auth] signInWithCustomToken:customToken
  1256. completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  1257. if (error) {
  1258. [self logFailure:@"sign-in with custom token failed" error:error];
  1259. [self logFailedTest:@"A fresh custom token should succeed in signing-in."];
  1260. return;
  1261. }
  1262. [self logSuccess:@"sign-in with custom token succeeded."];
  1263. [self updateEmailPasswordWithCompletion:^{
  1264. [self automatedBYOAuthDisplayNameAndPhotoURl];
  1265. }];
  1266. }];
  1267. }];
  1268. }];
  1269. }
  1270. /** @fn automatedBYOAuthDisplayNameAndPhotoURl
  1271. @brief Automatically executes the manual test for setting display name and photo url in BYOAuth.
  1272. */
  1273. - (void)automatedBYOAuthDisplayNameAndPhotoURl {
  1274. [self showSpinner:^{
  1275. NSError *error;
  1276. NSString *customToken = [NSString stringWithContentsOfURL:[NSURL URLWithString:kCustomTokenUrl]
  1277. encoding:NSUTF8StringEncoding
  1278. error:&error];
  1279. [self hideSpinner:^{
  1280. if (error) {
  1281. [self log:@"There was an error retrieving the custom token."];
  1282. return;
  1283. }
  1284. [[AppManager auth] signInWithCustomToken:customToken
  1285. completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  1286. if (error) {
  1287. [self logFailure:@"sign-in with custom token failed" error:error];
  1288. [self logFailedTest:@"A fresh custom token should succeed in signing-in."];
  1289. return;
  1290. }
  1291. [self logSuccess:@"sign-in with custom token succeeded."];
  1292. [self updateDisplayNameAndPhotoURlWithCompletion:^{
  1293. [self log:@"FINISHED AUTOMATED MANUAL TEST FOR BYOAUTH."];
  1294. [self reloadUser];
  1295. }];
  1296. }];
  1297. }];
  1298. }];
  1299. }
  1300. /** @fn updateEmailPasswordWithCompletion:
  1301. @brief Updates email and password for automatic manual tests, and signs user in with new email
  1302. and password.
  1303. @param completion The completion block to continue the automatic test flow.
  1304. */
  1305. - (void)updateEmailPasswordWithCompletion:(void(^)(void))completion {
  1306. FIRAuth *auth = [AppManager auth];
  1307. [auth.currentUser updateEmail:kFakeEmail completion:^(NSError *_Nullable error) {
  1308. if (error) {
  1309. [self logFailure:@"update email failed" error:error];
  1310. [self logFailedTest:@"Update email should succeed when properly signed-in."];
  1311. return;
  1312. }
  1313. [self logSuccess:@"update email succeeded."];
  1314. [auth.currentUser updatePassword:kFakePassword completion:^(NSError *_Nullable error) {
  1315. if (error) {
  1316. [self logFailure:@"update password failed" error:error];
  1317. [self logFailedTest:@"Update password should succeed when properly signed-in."];
  1318. return;
  1319. }
  1320. [self logSuccess:@"update password succeeded."];
  1321. [auth signOut:NULL];
  1322. FIRAuthCredential *credential =
  1323. [FIREmailAuthProvider credentialWithEmail:kFakeEmail password:kFakePassword];
  1324. [auth signInWithCredential:credential
  1325. completion:^(FIRUser *_Nullable user,
  1326. NSError *_Nullable error) {
  1327. if (error) {
  1328. [self logFailure:@"sign-in with Email/Password failed" error:error];
  1329. [self logFailedTest:@"sign-in with Email/Password should succeed."];
  1330. return;
  1331. }
  1332. [self logSuccess:@"sign-in with Email/Password succeeded."];
  1333. // Delete the user so that we can reuse the fake email address for subsequent tests.
  1334. [auth.currentUser deleteWithCompletion:^(NSError *_Nullable error) {
  1335. if (error) {
  1336. [self logFailure:@"Failed to delete user." error:error];
  1337. [self logFailedTest:@"Deleting a user that was recently signed-in should succeed"];
  1338. return;
  1339. }
  1340. [self logSuccess:@"User deleted."];
  1341. completion();
  1342. }];
  1343. }];
  1344. }];
  1345. }];
  1346. }
  1347. /** @fn updateDisplayNameAndPhotoURlWithCompletion:
  1348. @brief Automatically executes the manual test for setting displayName and photoUrl.
  1349. @param completion The completion block to continue the automatic test flow.
  1350. */
  1351. - (void)updateDisplayNameAndPhotoURlWithCompletion:(void(^)(void))completion {
  1352. FIRAuth *auth = [AppManager auth];
  1353. FIRUserProfileChangeRequest *changeRequest = [auth.currentUser profileChangeRequest];
  1354. changeRequest.photoURL = [NSURL URLWithString:kFakeDisplayPhotoUrl];
  1355. [changeRequest commitChangesWithCompletion:^(NSError *_Nullable error) {
  1356. if (error) {
  1357. [self logFailure:@"set photo URL failed" error:error];
  1358. [self logFailedTest:@"Change photo Url should succeed when signed-in."];
  1359. return;
  1360. }
  1361. [self logSuccess:@"set PhotoURL succeeded."];
  1362. FIRUserProfileChangeRequest *changeRequest = [auth.currentUser profileChangeRequest];
  1363. changeRequest.displayName = kFakeDisplayName;
  1364. [changeRequest commitChangesWithCompletion:^(NSError *_Nullable error) {
  1365. if (error) {
  1366. [self logFailure:@"set display name failed" error:error];
  1367. [self logFailedTest:@"Change display name should succeed when signed-in."];
  1368. return;
  1369. }
  1370. [self logSuccess:@"set display name succeeded."];
  1371. completion();
  1372. }];
  1373. }];
  1374. }
  1375. /** @fn addAuthStateListener
  1376. @brief Adds an auth state did change listener (block).
  1377. */
  1378. - (void)addAuthStateListener {
  1379. __weak typeof(self) weakSelf = self;
  1380. NSUInteger index = _authStateDidChangeListeners.count;
  1381. [self log:[NSString stringWithFormat:@"Auth State Did Change Listener #%lu was added.",
  1382. (unsigned long)index]];
  1383. FIRAuthStateDidChangeListenerHandle handle =
  1384. [[AppManager auth] addAuthStateDidChangeListener:^(FIRAuth *_Nonnull auth,
  1385. FIRUser *_Nullable user) {
  1386. [weakSelf log:[NSString stringWithFormat:
  1387. @"Auth State Did Change Listener #%lu was invoked on user '%@'.",
  1388. (unsigned long)index, user.uid]];
  1389. }];
  1390. [_authStateDidChangeListeners addObject:handle];
  1391. }
  1392. /** @fn removeAuthStateListener
  1393. @brief Removes an auth state did change listener (block).
  1394. */
  1395. - (void)removeAuthStateListener {
  1396. if (!_authStateDidChangeListeners.count) {
  1397. [self log:@"No remaining Auth State Did Change Listeners."];
  1398. return;
  1399. }
  1400. NSUInteger index = _authStateDidChangeListeners.count - 1;
  1401. FIRAuthStateDidChangeListenerHandle handle = _authStateDidChangeListeners.lastObject;
  1402. [[AppManager auth] removeAuthStateDidChangeListener:handle];
  1403. [_authStateDidChangeListeners removeObject:handle];
  1404. NSString *logString =
  1405. [NSString stringWithFormat:@"Auth State Did Change Listener #%lu was removed.",
  1406. (unsigned long)index];
  1407. [self log:logString];
  1408. }
  1409. /** @fn addIDTokenListener
  1410. @brief Adds an ID token did change listener (block).
  1411. */
  1412. - (void)addIDTokenListener {
  1413. __weak typeof(self) weakSelf = self;
  1414. NSUInteger index = _IDTokenDidChangeListeners.count;
  1415. [self log:[NSString stringWithFormat:@"ID Token Did Change Listener #%lu was added.",
  1416. (unsigned long)index]];
  1417. FIRIDTokenDidChangeListenerHandle handle =
  1418. [[AppManager auth] addIDTokenDidChangeListener:^(FIRAuth *_Nonnull auth,
  1419. FIRUser *_Nullable user) {
  1420. [weakSelf log:[NSString stringWithFormat:
  1421. @"ID Token Did Change Listener #%lu was invoked on user '%@'.",
  1422. (unsigned long)index, user.uid]];
  1423. }];
  1424. [_IDTokenDidChangeListeners addObject:handle];
  1425. }
  1426. /** @fn removeIDTokenListener
  1427. @brief Removes an ID token did change listener (block).
  1428. */
  1429. - (void)removeIDTokenListener {
  1430. if (!_IDTokenDidChangeListeners.count) {
  1431. [self log:@"No remaining ID Token Did Change Listeners."];
  1432. return;
  1433. }
  1434. NSUInteger index = _IDTokenDidChangeListeners.count - 1;
  1435. FIRIDTokenDidChangeListenerHandle handle = _IDTokenDidChangeListeners.lastObject;
  1436. [[AppManager auth] removeIDTokenDidChangeListener:handle];
  1437. [_IDTokenDidChangeListeners removeObject:handle];
  1438. NSString *logString =
  1439. [NSString stringWithFormat:@"ID Token Did Change Listener #%lu was removed.",
  1440. (unsigned long)index];
  1441. [self log:logString];
  1442. }
  1443. /** @fn log:
  1444. @brief Prints a log message to the sample app console.
  1445. @param string The string to add to the console.
  1446. */
  1447. - (void)log:(NSString *)string {
  1448. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  1449. dateFormatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
  1450. NSString *date = [dateFormatter stringFromDate:[NSDate date]];
  1451. if (!_consoleString) {
  1452. _consoleString = [NSMutableString string];
  1453. }
  1454. [_consoleString appendString:[NSString stringWithFormat:@"%@ %@\n", date, string]];
  1455. _consoleTextView.text = _consoleString;
  1456. CGRect targetRect = CGRectMake(0, _consoleTextView.contentSize.height - 1, 1, 1);
  1457. [_consoleTextView scrollRectToVisible:targetRect animated:YES];
  1458. }
  1459. /** @fn logSuccess:
  1460. @brief Wraps a string into a succeful log message format.
  1461. @param string Part of the log message.
  1462. @remarks The @string parameter should be a string ending with a period, as it is the end of the
  1463. log message.
  1464. */
  1465. - (void)logSuccess:(NSString *)string {
  1466. [self log:[NSString stringWithFormat:@"SUCCESS: %@", string]];
  1467. }
  1468. /** @fn logFailure:
  1469. @brief Wraps a string into a failed log message format.
  1470. @param string Part of the message to wrap.
  1471. @remarks The @string parameter should be a string that NEVER ends with a period, as it is
  1472. guaranteed not to be the last part fo the log message.
  1473. */
  1474. - (void)logFailure:(NSString *)string error:(NSError *) error {
  1475. NSString *message =
  1476. [NSString stringWithFormat:@"FAILURE: %@ Error Description: %@.", string, error.description];
  1477. [self log:message];
  1478. }
  1479. /** @fn logTestFailed
  1480. @brief Logs test failure to the console.
  1481. @param reason The reason why the test is considered a failure.
  1482. @remarks The calling method should immediately terminate after invoking this method i.e by
  1483. return statement or end of fucntions. The @reason parameter should be a string ending with a
  1484. period, as it is the end of the log message.
  1485. */
  1486. - (void)logFailedTest:( NSString *_Nonnull )reason {
  1487. [self log:[NSString stringWithFormat:@"FAILIURE: TEST FAILED - %@", reason]];
  1488. }
  1489. /** @fn presentSettings
  1490. @brief Invoked when the settings row is pressed.
  1491. */
  1492. - (void)presentSettings {
  1493. SettingsViewController *settingsViewController = [[SettingsViewController alloc]
  1494. initWithNibName:NSStringFromClass([SettingsViewController class])
  1495. bundle:nil];
  1496. [self presentViewController:settingsViewController animated:YES completion:nil];
  1497. }
  1498. /** @fn presentUserInfo
  1499. @brief Invoked when the user info row is pressed.
  1500. */
  1501. - (void)presentUserInfo {
  1502. UserInfoViewController *userInfoViewController =
  1503. [[UserInfoViewController alloc] initWithUser:[AppManager auth].currentUser];
  1504. [self presentViewController:userInfoViewController animated:YES completion:nil];
  1505. }
  1506. /** @fn presentUserInMemoryInfo
  1507. @brief Invoked when the user in memory info row is pressed.
  1508. */
  1509. - (void)presentUserInMemoryInfo {
  1510. UserInfoViewController *userInfoViewController =
  1511. [[UserInfoViewController alloc] initWithUser:_userInMemory];
  1512. [self presentViewController:userInfoViewController animated:YES completion:nil];
  1513. }
  1514. /** @fn signInGoogle
  1515. @brief Invoked when "Sign in with Google" row is pressed.
  1516. */
  1517. - (void)signInGoogle {
  1518. [self signinWithProvider:[AuthProviders google] retrieveData:NO];
  1519. }
  1520. /** @fn signInGoogleAndRetrieveData
  1521. @brief Invoked when "Sign in with Google and retrieve data" row is pressed.
  1522. */
  1523. - (void)signInGoogleAndRetrieveData {
  1524. [self signinWithProvider:[AuthProviders google] retrieveData:YES];
  1525. }
  1526. /** @fn signInFacebook
  1527. @brief Invoked when "Sign in with Facebook" row is pressed.
  1528. */
  1529. - (void)signInFacebook {
  1530. [self signinWithProvider:[AuthProviders facebook] retrieveData:NO];
  1531. }
  1532. /** @fn signInFacebookAndRetrieveData
  1533. @brief Invoked when "Sign in with Facebook and retrieve data" row is pressed.
  1534. */
  1535. - (void)signInFacebookAndRetrieveData {
  1536. [self signinWithProvider:[AuthProviders facebook] retrieveData:YES];
  1537. }
  1538. /** @fn signInEmailPassword
  1539. @brief Invoked when "Sign in with Email/Password" row is pressed.
  1540. */
  1541. - (void)signInEmailPassword {
  1542. [self showTextInputPromptWithMessage:@"Email Address:"
  1543. keyboardType:UIKeyboardTypeEmailAddress
  1544. completionBlock:^(BOOL userPressedOK, NSString *_Nullable email) {
  1545. if (!userPressedOK || !email.length) {
  1546. return;
  1547. }
  1548. [self showTextInputPromptWithMessage:@"Password:"
  1549. completionBlock:^(BOOL userPressedOK, NSString *_Nullable password) {
  1550. if (!userPressedOK) {
  1551. return;
  1552. }
  1553. FIRAuthCredential *credential =
  1554. [FIREmailAuthProvider credentialWithEmail:email
  1555. password:password];
  1556. [self showSpinner:^{
  1557. [[AppManager auth] signInWithCredential:credential
  1558. completion:^(FIRUser *_Nullable user,
  1559. NSError *_Nullable error) {
  1560. [self hideSpinner:^{
  1561. if (error) {
  1562. [self logFailure:@"sign-in with Email/Password failed" error:error];
  1563. } else {
  1564. [self logSuccess:@"sign-in with Email/Password succeeded."];
  1565. }
  1566. [self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In Error" error:error];
  1567. }];
  1568. }];
  1569. }];
  1570. }];
  1571. }];
  1572. }
  1573. - (void)signInEmailPasswordAuthDataResult {
  1574. [self showTextInputPromptWithMessage:@"Email Address:"
  1575. keyboardType:UIKeyboardTypeEmailAddress
  1576. completionBlock:^(BOOL userPressedOK, NSString *_Nullable email) {
  1577. if (!userPressedOK || !email.length) {
  1578. return;
  1579. }
  1580. [self showTextInputPromptWithMessage:@"Password:"
  1581. completionBlock:^(BOOL userPressedOK, NSString *_Nullable password) {
  1582. if (!userPressedOK) {
  1583. return;
  1584. }
  1585. [self showSpinner:^{
  1586. [[AppManager auth] signInAndRetrieveDataWithEmail:email
  1587. password:password
  1588. completion:^(FIRAuthDataResult *_Nullable authResult,
  1589. NSError *_Nullable error) {
  1590. [self hideSpinner:^{
  1591. if (error) {
  1592. [self logFailure:@"sign-in with Email/Password failed" error:error];
  1593. } else {
  1594. [self logSuccess:@"sign-in with Email/Password succeeded."];
  1595. [self log:[NSString stringWithFormat:@"UID: %@",authResult.user.uid]];
  1596. }
  1597. [self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In Error" error:error];
  1598. }];
  1599. }];
  1600. }];
  1601. }];
  1602. }];
  1603. }
  1604. /** @fn signInWithEmailLink
  1605. @brief Invoked when "Sign in with email link" row is pressed.
  1606. */
  1607. - (void)signInWithEmailLink {
  1608. [self showTextInputPromptWithMessage:@"Email Address:"
  1609. keyboardType:UIKeyboardTypeEmailAddress
  1610. completionBlock:^(BOOL userPressedOK, NSString *_Nullable email) {
  1611. if (!userPressedOK || !email.length) {
  1612. return;
  1613. }
  1614. [self showTextInputPromptWithMessage:@"Email Sign-In Link:"
  1615. completionBlock:^(BOOL userPressedOK, NSString *_Nullable link) {
  1616. if (!userPressedOK) {
  1617. return;
  1618. }
  1619. if ([[FIRAuth auth] isSignInWithEmailLink:link]) {
  1620. [self showSpinner:^{
  1621. [[AppManager auth] signInWithEmail:email
  1622. link:link
  1623. completion:^(FIRAuthDataResult *_Nullable authResult,
  1624. NSError *_Nullable error) {
  1625. [self hideSpinner:^{
  1626. if (error) {
  1627. [self logFailure:@"sign-in with Email/Sign-In failed" error:error];
  1628. } else {
  1629. [self logSuccess:@"sign-in with Email/Sign-In link succeeded."];
  1630. [self log:[NSString stringWithFormat:@"UID: %@",authResult.user.uid]];
  1631. }
  1632. [self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In Error" error:error];
  1633. }];
  1634. }];
  1635. }];
  1636. } else {
  1637. [self log:@"The sign-in link is invalid"];
  1638. }
  1639. }];
  1640. }];
  1641. }
  1642. /** @fn verifyEmailLinkAccount
  1643. @brief Invoked to verify that the current user is an email-link user.
  1644. */
  1645. - (void)verifyEmailLinkAccount {
  1646. if (![FIRAuth auth].currentUser.email) {
  1647. [self showMessagePrompt:@"There is no signed-in user available."];
  1648. return;
  1649. }
  1650. [[FIRAuth auth] fetchSignInMethodsForEmail:[FIRAuth auth].currentUser.email
  1651. completion:^(NSArray<NSString *> *_Nullable signInMethods,
  1652. NSError *_Nullable error) {
  1653. if (error) {
  1654. [self showMessagePrompt:@"There was an error fetching sign-in methods."];
  1655. return;
  1656. }
  1657. if (![signInMethods containsObject:FIREmailLinkAuthSignInMethod]) {
  1658. [self showMessagePrompt:@"Error: The current user is NOT an email-link user."];
  1659. return;
  1660. }
  1661. [self showMessagePrompt:@"The current user is an email-link user."];
  1662. }];
  1663. }
  1664. /** @fn sendEmailSignInLink
  1665. @brief Invoked when "Send email sign-in link" row is pressed.
  1666. */
  1667. - (void)sendEmailSignInLink {
  1668. [self showTextInputPromptWithMessage:@"Email:"
  1669. completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  1670. if (!userPressedOK) {
  1671. return;
  1672. }
  1673. [self showSpinner:^{
  1674. void (^requestEmailSignInLink)(void (^)(NSError *)) = ^(void (^completion)(NSError *)) {
  1675. [[AppManager auth] sendSignInLinkToEmail:userInput
  1676. actionCodeSettings:[self actionCodeSettings]
  1677. completion:completion];
  1678. };
  1679. requestEmailSignInLink(^(NSError *_Nullable error) {
  1680. [self hideSpinner:^{
  1681. if (error) {
  1682. [self logFailure:@"Email Link request failed" error:error];
  1683. [self showMessagePrompt:error.localizedDescription];
  1684. return;
  1685. }
  1686. [self logSuccess:@"Email Link request succeeded."];
  1687. [self showMessagePrompt:@"Sent"];
  1688. }];
  1689. });
  1690. }];
  1691. }];
  1692. }
  1693. /** @fn signUpNewEmail
  1694. @brief Invoked if sign-in is attempted with new email/password.
  1695. @remarks Should only be called if @c FIRAuthErrorCodeInvalidEmail is encountered on attepmt to
  1696. sign in with email/password.
  1697. */
  1698. - (void)signUpNewEmail:(NSString *)email
  1699. password:(NSString *)password
  1700. callback:(nullable FIRAuthResultCallback)callback {
  1701. [[AppManager auth] createUserWithEmail:email
  1702. password:password
  1703. completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  1704. if (error) {
  1705. [self logFailure:@"sign-up with Email/Password failed" error:error];
  1706. if (callback) {
  1707. callback(nil, error);
  1708. }
  1709. } else {
  1710. [self logSuccess:@"sign-up with Email/Password succeeded."];
  1711. if (callback) {
  1712. callback(user, nil);
  1713. }
  1714. }
  1715. [self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In" error:error];
  1716. }];
  1717. }
  1718. /** @fn signInWithCustomToken
  1719. @brief Signs the user in using a manually-entered custom token.
  1720. */
  1721. - (void)signInWithCustomToken {
  1722. CustomTokenDataEntryViewControllerCompletion action =
  1723. ^(BOOL cancelled, NSString *_Nullable userEnteredTokenText) {
  1724. if (cancelled) {
  1725. [self log:@"CANCELLED:sign-in with custom token cancelled."];
  1726. return;
  1727. }
  1728. [self doSignInWithCustomToken:userEnteredTokenText];
  1729. };
  1730. CustomTokenDataEntryViewController *dataEntryViewController =
  1731. [[CustomTokenDataEntryViewController alloc] initWithCompletion:action];
  1732. [self presentViewController:dataEntryViewController animated:YES completion:nil];
  1733. }
  1734. /** @fn signInWithCustomTokenAuthResult
  1735. @brief Signs the user in using a manually-entered custom token.
  1736. */
  1737. - (void)signInWithCustomTokenAuthResult {
  1738. CustomTokenDataEntryViewControllerCompletion action =
  1739. ^(BOOL cancelled, NSString *_Nullable userEnteredTokenText) {
  1740. if (cancelled) {
  1741. [self log:@"CANCELLED:sign-in with custom token cancelled."];
  1742. return;
  1743. }
  1744. [self doSignInAndRetrieveDataWithCustomToken:userEnteredTokenText];
  1745. };
  1746. CustomTokenDataEntryViewController *dataEntryViewController =
  1747. [[CustomTokenDataEntryViewController alloc] initWithCompletion:action];
  1748. [self presentViewController:dataEntryViewController animated:YES completion:nil];
  1749. }
  1750. /** @fn signOut
  1751. @brief Signs the user out.
  1752. */
  1753. - (void)signOut {
  1754. [[AuthProviders google] signOut];
  1755. [[AuthProviders facebook] signOut];
  1756. [[AppManager auth] signOut:NULL];
  1757. }
  1758. /** @fn deleteAccount
  1759. @brief Deletes the current user account and signs the user out.
  1760. */
  1761. - (void)deleteAccount {
  1762. FIRUser *user = [self user];
  1763. [user deleteWithCompletion:^(NSError *_Nullable error) {
  1764. if (error) {
  1765. [self logFailure:@"delete account failed" error:error];
  1766. }
  1767. [self showTypicalUIForUserUpdateResultsWithTitle:kDeleteUserText error:error];
  1768. }];
  1769. }
  1770. /** @fn reauthenticateGoogle
  1771. @brief Asks the user to reauthenticate with Google.
  1772. */
  1773. - (void)reauthenticateGoogle {
  1774. [self reauthenticate:[AuthProviders google] retrieveData:NO];
  1775. }
  1776. /** @fn reauthenticateGoogleAndRetrieveData
  1777. @brief Asks the user to reauthenticate with Google and retrieve additional data.
  1778. */
  1779. - (void)reauthenticateGoogleAndRetrieveData {
  1780. [self reauthenticate:[AuthProviders google] retrieveData:YES];
  1781. }
  1782. /** @fn reauthenticateFB
  1783. @brief Asks the user to reauthenticate with Facebook.
  1784. */
  1785. - (void)reauthenticateFB {
  1786. [self reauthenticate:[AuthProviders facebook] retrieveData:NO];
  1787. }
  1788. /** @fn reauthenticateFBAndRetrieveData
  1789. @brief Asks the user to reauthenticate with Facebook and retrieve additional data.
  1790. */
  1791. - (void)reauthenticateFBAndRetrieveData {
  1792. [self reauthenticate:[AuthProviders facebook] retrieveData:YES];
  1793. }
  1794. /** @fn reauthenticateEmailPassword
  1795. @brief Asks the user to reauthenticate with email/password.
  1796. */
  1797. - (void)reauthenticateEmailPassword {
  1798. FIRUser *user = [self user];
  1799. if (!user) {
  1800. NSString *title = @"Missing User";
  1801. NSString *message = @"There is no signed-in email/password user.";
  1802. [self showMessagePromptWithTitle:title message:message showCancelButton:NO completion:nil];
  1803. return;
  1804. }
  1805. [self showEmailPasswordDialogWithCompletion:^(FIRAuthCredential *credential) {
  1806. [self showSpinner:^{
  1807. [[self user] reauthenticateWithCredential:credential
  1808. completion:^(NSError *_Nullable error) {
  1809. if (error) {
  1810. [self logFailure:@"reauthicate with email/password failed" error:error];
  1811. } else {
  1812. [self logSuccess:@"reauthicate with email/password succeeded."];
  1813. }
  1814. [self hideSpinner:^{
  1815. [self showTypicalUIForUserUpdateResultsWithTitle:kReauthenticateEmailText error:error];
  1816. }];
  1817. }];
  1818. }];
  1819. }];
  1820. }
  1821. /** @fn reauthenticate:
  1822. @brief Reauthenticates user.
  1823. @param authProvider The auth provider to use for reauthentication.
  1824. @param retrieveData Defines if additional provider data should be read.
  1825. */
  1826. - (void)reauthenticate:(id<AuthProvider>)authProvider retrieveData:(BOOL)retrieveData {
  1827. FIRUser *user = [self user];
  1828. if (!user) {
  1829. NSString *provider = @"Firebase";
  1830. if ([authProvider isKindOfClass:[GoogleAuthProvider class]]) {
  1831. provider = @"Google";
  1832. } else if ([authProvider isKindOfClass:[FacebookAuthProvider class]]) {
  1833. provider = @"Facebook";
  1834. }
  1835. NSString *title = @"Missing User";
  1836. NSString *message =
  1837. [NSString stringWithFormat:@"There is no signed-in %@ user.", provider];
  1838. [self showMessagePromptWithTitle:title message:message showCancelButton:NO completion:nil];
  1839. return;
  1840. }
  1841. [authProvider getAuthCredentialWithPresentingViewController:self
  1842. callback:^(FIRAuthCredential *credential,
  1843. NSError *error) {
  1844. if (credential) {
  1845. FIRAuthDataResultCallback completion = ^(FIRAuthDataResult *_Nullable authResult,
  1846. NSError *_Nullable error) {
  1847. if (error) {
  1848. [self logFailure:@"reauthenticate operation failed" error:error];
  1849. } else {
  1850. [self logSuccess:@"reauthenticate operation succeeded."];
  1851. }
  1852. if (authResult.additionalUserInfo) {
  1853. [self logSuccess:[self stringWithAdditionalUserInfo:authResult.additionalUserInfo]];
  1854. }
  1855. [self showTypicalUIForUserUpdateResultsWithTitle:@"Reauthenticate" error:error];
  1856. };
  1857. FIRUserProfileChangeCallback callback = ^(NSError *_Nullable error) {
  1858. completion(nil, error);
  1859. };
  1860. if (retrieveData) {
  1861. [user reauthenticateAndRetrieveDataWithCredential:credential completion:completion];
  1862. } else {
  1863. [user reauthenticateWithCredential:credential completion:callback];
  1864. }
  1865. }
  1866. }];
  1867. }
  1868. /** @fn signinWithProvider:
  1869. @brief Signs in the user with provided auth provider.
  1870. @param authProvider The auth provider to use for sign-in.
  1871. @param retrieveData Defines if additional provider data should be read.
  1872. */
  1873. - (void)signinWithProvider:(id<AuthProvider>)authProvider retrieveData:(BOOL)retrieveData {
  1874. FIRAuth *auth = [AppManager auth];
  1875. if (!auth) {
  1876. return;
  1877. }
  1878. [authProvider getAuthCredentialWithPresentingViewController:self
  1879. callback:^(FIRAuthCredential *credential,
  1880. NSError *error) {
  1881. if (credential) {
  1882. FIRAuthDataResultCallback completion = ^(FIRAuthDataResult *_Nullable authResult,
  1883. NSError *_Nullable error) {
  1884. if (error) {
  1885. [self logFailure:@"sign-in with provider failed" error:error];
  1886. } else {
  1887. [self logSuccess:@"sign-in with provider succeeded."];
  1888. }
  1889. if (authResult.additionalUserInfo) {
  1890. [self logSuccess:[self stringWithAdditionalUserInfo:authResult.additionalUserInfo]];
  1891. if (_isNewUserToggleOn) {
  1892. NSString *newUserString = authResult.additionalUserInfo.isNewUser ?
  1893. @"New user" : @"Existing user";
  1894. [self showMessagePromptWithTitle:@"New or Existing"
  1895. message:newUserString
  1896. showCancelButton:NO
  1897. completion:nil];
  1898. }
  1899. }
  1900. [self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In" error:error];
  1901. };
  1902. FIRAuthResultCallback callback = ^(FIRUser *_Nullable user,
  1903. NSError *_Nullable error) {
  1904. completion(nil, error);
  1905. };
  1906. if (retrieveData) {
  1907. [auth signInAndRetrieveDataWithCredential:credential completion:completion];
  1908. } else {
  1909. [auth signInWithCredential:credential completion:callback];
  1910. }
  1911. }
  1912. }];
  1913. }
  1914. /** @fn tokenCallback
  1915. @return A callback block to show the token.
  1916. */
  1917. - (FIRAuthTokenCallback)tokenCallback {
  1918. return ^(NSString *_Nullable token, NSError *_Nullable error) {
  1919. if (error) {
  1920. [self showMessagePromptWithTitle:kTokenRefreshErrorAlertTitle
  1921. message:error.localizedDescription
  1922. showCancelButton:NO
  1923. completion:nil];
  1924. [self logFailure:@"refresh token failed" error:error];
  1925. return;
  1926. }
  1927. [self logSuccess:@"refresh token succeeded."];
  1928. [self showMessagePromptWithTitle:kTokenRefreshedAlertTitle
  1929. message:token
  1930. showCancelButton:NO
  1931. completion:nil];
  1932. };
  1933. }
  1934. /** @fn getUserTokenWithForce:
  1935. @brief Gets the token from @c FIRUser, optionally a refreshed one.
  1936. @param force Whether the refresh is forced or not.
  1937. */
  1938. - (void)getUserTokenWithForce:(BOOL)force {
  1939. [[self user] getIDTokenForcingRefresh:force completion:[self tokenCallback]];
  1940. }
  1941. /** @fn getUserTokenResultWithForce:
  1942. @brief Gets the token result object from @c FIRUser, optionally a refreshed one.
  1943. @param force Whether the refresh is forced or not.
  1944. */
  1945. - (void)getUserTokenResultWithForce:(BOOL)force {
  1946. [[self user] getIDTokenResultForcingRefresh:force
  1947. completion:^(FIRAuthTokenResult *_Nullable tokenResult,
  1948. NSError *_Nullable error) {
  1949. if (error) {
  1950. [self showMessagePromptWithTitle:kTokenRefreshErrorAlertTitle
  1951. message:error.localizedDescription
  1952. showCancelButton:NO
  1953. completion:nil];
  1954. [self logFailure:@"refresh token failed" error:error];
  1955. return;
  1956. }
  1957. [self logSuccess:@"refresh token succeeded."];
  1958. NSMutableString *message =
  1959. [[NSMutableString alloc] initWithString:
  1960. [NSString stringWithFormat:@"Token : %@\n", tokenResult.token]];
  1961. [message appendString:[NSString stringWithFormat:@"Auth Date : %@\n", tokenResult.authDate]];
  1962. [message appendString:
  1963. [NSString stringWithFormat:@"EXP Date : %@\n", tokenResult.expirationDate]];
  1964. [message appendString:
  1965. [NSString stringWithFormat:@"Issued Date : %@\n", tokenResult.issuedAtDate]];
  1966. [self showMessagePromptWithTitle:kTokenRefreshedAlertTitle
  1967. message:message
  1968. showCancelButton:NO
  1969. completion:nil];
  1970. }];
  1971. }
  1972. /** @fn getAppTokenWithForce:
  1973. @brief Gets the token from @c FIRApp , optionally a refreshed one.
  1974. @param force Whether the refresh is forced or not.
  1975. */
  1976. - (void)getAppTokenWithForce:(BOOL)force {
  1977. [[FIRApp defaultApp] getTokenForcingRefresh:force withCallback:[self tokenCallback]];
  1978. }
  1979. /** @fn setDisplayName
  1980. @brief Changes the display name of the current user.
  1981. */
  1982. - (void)setDisplayName {
  1983. [self showTextInputPromptWithMessage:@"Display Name:"
  1984. completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  1985. if (!userPressedOK || !userInput.length) {
  1986. return;
  1987. }
  1988. [self showSpinner:^{
  1989. FIRUserProfileChangeRequest *changeRequest = [[self user] profileChangeRequest];
  1990. changeRequest.displayName = userInput;
  1991. [changeRequest commitChangesWithCompletion:^(NSError *_Nullable error) {
  1992. [self hideSpinner:^{
  1993. if (error) {
  1994. [self logFailure:@"set display name failed" error:error];
  1995. } else {
  1996. [self logSuccess:@"set display name succeeded."];
  1997. }
  1998. [self showTypicalUIForUserUpdateResultsWithTitle:kSetDisplayNameTitle error:error];
  1999. }];
  2000. }];
  2001. }];
  2002. }];
  2003. }
  2004. /** @fn setPhotoURL
  2005. @brief Changes the photo url of the current user.
  2006. */
  2007. - (void)setPhotoURL {
  2008. [self showTextInputPromptWithMessage:@"Photo URL:"
  2009. completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  2010. if (!userPressedOK || !userInput.length) {
  2011. return;
  2012. }
  2013. [self showSpinner:^{
  2014. FIRUserProfileChangeRequest *changeRequest = [[self user] profileChangeRequest];
  2015. changeRequest.photoURL = [NSURL URLWithString:userInput];
  2016. [changeRequest commitChangesWithCompletion:^(NSError *_Nullable error) {
  2017. if (error) {
  2018. [self logFailure:@"set photo URL failed" error:error];
  2019. } else {
  2020. [self logSuccess:@"set Photo URL succeeded."];
  2021. }
  2022. [self hideSpinner:^{
  2023. [self showTypicalUIForUserUpdateResultsWithTitle:kSetPhotoURLText error:error];
  2024. }];
  2025. }];
  2026. }];
  2027. }];
  2028. }
  2029. /** @fn reloadUser
  2030. @brief Reloads the user from server.
  2031. */
  2032. - (void)reloadUser {
  2033. [self showSpinner:^() {
  2034. [[self user] reloadWithCompletion:^(NSError *_Nullable error) {
  2035. if (error) {
  2036. [self logFailure:@"reload user failed" error:error];
  2037. } else {
  2038. [self logSuccess:@"reload user succeeded."];
  2039. }
  2040. [self hideSpinner:^() {
  2041. [self showTypicalUIForUserUpdateResultsWithTitle:kReloadText error:error];
  2042. }];
  2043. }];
  2044. }];
  2045. }
  2046. /** @fn linkWithAuthProvider:retrieveData:
  2047. @brief Asks the user to sign in with an auth provider and link the current user with it.
  2048. @param authProvider The auth provider to sign in and link with.
  2049. @param retrieveData Defines if additional provider data should be read.
  2050. */
  2051. - (void)linkWithAuthProvider:(id<AuthProvider>)authProvider retrieveData:(BOOL)retrieveData {
  2052. FIRUser *user = [self user];
  2053. if (!user) {
  2054. return;
  2055. }
  2056. [authProvider getAuthCredentialWithPresentingViewController:self
  2057. callback:^(FIRAuthCredential *credential,
  2058. NSError *error) {
  2059. if (credential) {
  2060. FIRAuthDataResultCallback completion = ^(FIRAuthDataResult *_Nullable authResult,
  2061. NSError *_Nullable error) {
  2062. if (error) {
  2063. [self logFailure:@"link auth provider failed" error:error];
  2064. } else {
  2065. [self logSuccess:@"link auth provider succeeded."];
  2066. }
  2067. if (authResult.additionalUserInfo) {
  2068. [self logSuccess:[self stringWithAdditionalUserInfo:authResult.additionalUserInfo]];
  2069. }
  2070. if (retrieveData) {
  2071. [self showUIForAuthDataResultWithResult:authResult error:error];
  2072. } else {
  2073. [self showTypicalUIForUserUpdateResultsWithTitle:@"Link Account" error:error];
  2074. }
  2075. };
  2076. FIRAuthResultCallback callback = ^(FIRUser *_Nullable user,
  2077. NSError *_Nullable error) {
  2078. completion(nil, error);
  2079. };
  2080. if (retrieveData) {
  2081. [user linkAndRetrieveDataWithCredential:credential completion:completion];
  2082. } else {
  2083. [user linkWithCredential:credential completion:callback];
  2084. }
  2085. }
  2086. }];
  2087. }
  2088. /** @fn linkWithGoogle
  2089. @brief Asks the user to sign in with Google and link the current user with this Google account.
  2090. */
  2091. - (void)linkWithGoogle {
  2092. [self linkWithAuthProvider:[AuthProviders google] retrieveData:NO];
  2093. }
  2094. /** @fn linkWithGoogleAndRetrieveData
  2095. @brief Asks the user to sign in with Google and link the current user with this Google account
  2096. and retrieve additional data.
  2097. */
  2098. - (void)linkWithGoogleAndRetrieveData {
  2099. [self linkWithAuthProvider:[AuthProviders google] retrieveData:YES];
  2100. }
  2101. /** @fn linkWithFacebook
  2102. @brief Asks the user to sign in with Facebook and link the current user with this Facebook
  2103. account.
  2104. */
  2105. - (void)linkWithFacebook {
  2106. [self linkWithAuthProvider:[AuthProviders facebook] retrieveData:NO];
  2107. }
  2108. /** @fn linkWithFacebookAndRetrieveData
  2109. @brief Asks the user to sign in with Facebook and link the current user with this Facebook
  2110. account and retrieve additional data.
  2111. */
  2112. - (void)linkWithFacebookAndRetrieveData {
  2113. [self linkWithAuthProvider:[AuthProviders facebook] retrieveData:YES];
  2114. }
  2115. /** @fn linkWithEmailPassword
  2116. @brief Asks the user to sign in with Facebook and link the current user with this Facebook
  2117. account.
  2118. */
  2119. - (void)linkWithEmailPassword {
  2120. [self showEmailPasswordDialogWithCompletion:^(FIRAuthCredential *credential) {
  2121. [self showSpinner:^{
  2122. [[self user] linkWithCredential:credential
  2123. completion:^(FIRUser *user, NSError *_Nullable error) {
  2124. if (error) {
  2125. [self logFailure:@"link Email/Password failed" error:error];
  2126. } else {
  2127. [self logSuccess:@"link Email/Password succeeded."];
  2128. }
  2129. [self hideSpinner:^{
  2130. [self showTypicalUIForUserUpdateResultsWithTitle:kLinkWithEmailPasswordText error:error];
  2131. }];
  2132. }];
  2133. }];
  2134. }];
  2135. }
  2136. /** @fn showEmailPasswordDialogWithCompletion:
  2137. @brief shows email/password input dialog.
  2138. @param completion The completion block that will do some operation on the credential email
  2139. /passwowrd credential obtained.
  2140. */
  2141. - (void)showEmailPasswordDialogWithCompletion:(ShowEmailPasswordDialogCompletion)completion {
  2142. [self showTextInputPromptWithMessage:@"Email Address:"
  2143. completionBlock:^(BOOL userPressedOK, NSString *_Nullable email) {
  2144. if (!userPressedOK || !email.length) {
  2145. return;
  2146. }
  2147. [self showTextInputPromptWithMessage:@"Password:"
  2148. completionBlock:^(BOOL userPressedOK, NSString *_Nullable password) {
  2149. if (!userPressedOK || !password.length) {
  2150. return;
  2151. }
  2152. FIRAuthCredential *credential = [FIREmailAuthProvider credentialWithEmail:email
  2153. password:password];
  2154. completion(credential);
  2155. }];
  2156. }];
  2157. }
  2158. /** @fn unlinkFromProvider:
  2159. @brief Unlinks the current user from the provider with the specified provider ID.
  2160. @param provider The provider ID of the provider to unlink the current user's account from.
  2161. @completion A completion block to be executed after the provider is unlinked.
  2162. */
  2163. - (void)unlinkFromProvider:(NSString *)provider
  2164. completion:(nullable testAutomationCallback)completion {
  2165. [[self user] unlinkFromProvider:provider
  2166. completion:^(FIRUser *_Nullable user,
  2167. NSError *_Nullable error) {
  2168. if (error) {
  2169. [self logFailure:@"unlink auth provider failed" error:error];
  2170. if (completion) {
  2171. completion(error);
  2172. }
  2173. return;
  2174. }
  2175. [self logSuccess:@"unlink auth provider succeeded."];
  2176. if (completion) {
  2177. completion(nil);
  2178. }
  2179. [self showTypicalUIForUserUpdateResultsWithTitle:kUnlinkTitle error:error];
  2180. }];
  2181. }
  2182. /** @fn getProvidersForEmail
  2183. @brief Prompts the user for an email address, calls @c FIRAuth.getProvidersForEmail:callback:
  2184. and displays the result.
  2185. */
  2186. - (void)getProvidersForEmail {
  2187. [self showTextInputPromptWithMessage:@"Email:"
  2188. completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  2189. if (!userPressedOK || !userInput.length) {
  2190. return;
  2191. }
  2192. [self showSpinner:^{
  2193. [[AppManager auth] fetchProvidersForEmail:userInput
  2194. completion:^(NSArray<NSString *> *_Nullable providers,
  2195. NSError *_Nullable error) {
  2196. if (error) {
  2197. [self logFailure:@"get providers for email failed" error:error];
  2198. } else {
  2199. [self logSuccess:@"get providers for email succeeded."];
  2200. }
  2201. [self hideSpinner:^{
  2202. if (error) {
  2203. [self showMessagePrompt:error.localizedDescription];
  2204. return;
  2205. }
  2206. [self showMessagePrompt:[providers componentsJoinedByString:@", "]];
  2207. }];
  2208. }];
  2209. }];
  2210. }];
  2211. }
  2212. /** @fn getAllSignInMethodsForEmail
  2213. @brief Prompts user for an email address, calls @c FIRAuth.getAllSignInMethodsForEmail:callback:
  2214. and displays the result.
  2215. */
  2216. - (void)getAllSignInMethodsForEmail {
  2217. [self showTextInputPromptWithMessage:@"Email:"
  2218. completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  2219. if (!userPressedOK || !userInput.length) {
  2220. return;
  2221. }
  2222. [self showSpinner:^{
  2223. [[AppManager auth] fetchSignInMethodsForEmail:userInput
  2224. completion:^(NSArray<NSString *> *_Nullable signInMethods,
  2225. NSError *_Nullable error) {
  2226. if (error) {
  2227. [self logFailure:@"get sign-in methods for email failed" error:error];
  2228. } else {
  2229. [self logSuccess:@"get sign-in methods for email succeeded."];
  2230. }
  2231. [self hideSpinner:^{
  2232. if (error) {
  2233. [self showMessagePrompt:error.localizedDescription];
  2234. return;
  2235. }
  2236. [self showMessagePrompt:[signInMethods componentsJoinedByString:@", "]];
  2237. }];
  2238. }];
  2239. }];
  2240. }];
  2241. }
  2242. /** @fn actionCodeRequestTypeString
  2243. @brief Returns a string description for the type of the next action code request.
  2244. */
  2245. - (NSString *)actionCodeRequestTypeString {
  2246. switch (_actionCodeRequestType) {
  2247. case ActionCodeRequestTypeInApp:
  2248. return @"In-App + Continue URL";
  2249. case ActionCodeRequestTypeContinue:
  2250. return @"Continue URL";
  2251. case ActionCodeRequestTypeEmail:
  2252. return @"Email Only";
  2253. }
  2254. }
  2255. /** @fn toggleActionCodeRequestType
  2256. @brief Toggle the next action code request type.
  2257. */
  2258. - (void)toggleActionCodeRequestType {
  2259. switch (_actionCodeRequestType) {
  2260. case ActionCodeRequestTypeInApp:
  2261. _actionCodeRequestType = ActionCodeRequestTypeContinue;
  2262. break;
  2263. case ActionCodeRequestTypeContinue:
  2264. _actionCodeRequestType = ActionCodeRequestTypeEmail;
  2265. break;
  2266. case ActionCodeRequestTypeEmail:
  2267. _actionCodeRequestType = ActionCodeRequestTypeInApp;
  2268. break;
  2269. }
  2270. [self updateTable];
  2271. }
  2272. - (void)changeActionCodeContinueURL {
  2273. [self showTextInputPromptWithMessage:kContinueURLDescription
  2274. completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  2275. if (userPressedOK) {
  2276. _actionCodeContinueURL = userInput.length ? [NSURL URLWithString:userInput] : nil;
  2277. [self updateTable];
  2278. }
  2279. }];
  2280. }
  2281. /** @fn requestVerifyEmail
  2282. @brief Requests a "verify email" email be sent.
  2283. */
  2284. - (void)requestVerifyEmail {
  2285. [self showSpinner:^{
  2286. void (^sendEmailVerification)(void (^)(NSError *)) = ^(void (^completion)(NSError *)) {
  2287. if (_actionCodeRequestType == ActionCodeRequestTypeEmail) {
  2288. [[self user] sendEmailVerificationWithCompletion:completion];
  2289. } else {
  2290. [[self user] sendEmailVerificationWithActionCodeSettings:[self actionCodeSettings]
  2291. completion:completion];
  2292. }
  2293. };
  2294. sendEmailVerification(^(NSError *_Nullable error) {
  2295. [self hideSpinner:^{
  2296. if (error) {
  2297. [self logFailure:@"request verify email failed" error:error];
  2298. [self showMessagePrompt:error.localizedDescription];
  2299. return;
  2300. }
  2301. [self logSuccess:@"request verify email succeeded."];
  2302. [self showMessagePrompt:@"Sent"];
  2303. }];
  2304. });
  2305. }];
  2306. }
  2307. /** @fn requestPasswordReset
  2308. @brief Requests a "password reset" email be sent.
  2309. */
  2310. - (void)requestPasswordReset {
  2311. [self showTextInputPromptWithMessage:@"Email:"
  2312. completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  2313. if (!userPressedOK || !userInput.length) {
  2314. return;
  2315. }
  2316. [self showSpinner:^{
  2317. void (^requestPasswordReset)(void (^)(NSError *)) = ^(void (^completion)(NSError *)) {
  2318. if (_actionCodeRequestType == ActionCodeRequestTypeEmail) {
  2319. [[AppManager auth] sendPasswordResetWithEmail:userInput completion:completion];
  2320. } else {
  2321. [[AppManager auth] sendPasswordResetWithEmail:userInput
  2322. actionCodeSettings:[self actionCodeSettings]
  2323. completion:completion];
  2324. }
  2325. };
  2326. requestPasswordReset(^(NSError *_Nullable error) {
  2327. [self hideSpinner:^{
  2328. if (error) {
  2329. [self logFailure:@"request password reset failed" error:error];
  2330. [self showMessagePrompt:error.localizedDescription];
  2331. return;
  2332. }
  2333. [self logSuccess:@"request password reset succeeded."];
  2334. [self showMessagePrompt:@"Sent"];
  2335. }];
  2336. });
  2337. }];
  2338. }];
  2339. }
  2340. /** @fn resetPassword
  2341. @brief Performs a password reset operation.
  2342. */
  2343. - (void)resetPassword {
  2344. [self showTextInputPromptWithMessage:@"OOB Code:"
  2345. completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  2346. if (!userPressedOK || !userInput.length) {
  2347. return;
  2348. }
  2349. NSString *code = userInput;
  2350. [self showTextInputPromptWithMessage:@"New Password:"
  2351. completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  2352. if (!userPressedOK || !userInput.length) {
  2353. return;
  2354. }
  2355. [self showSpinner:^{
  2356. [[AppManager auth] confirmPasswordResetWithCode:code
  2357. newPassword:userInput
  2358. completion:^(NSError *_Nullable error) {
  2359. [self hideSpinner:^{
  2360. if (error) {
  2361. [self logFailure:@"Password reset failed" error:error];
  2362. [self showMessagePrompt:error.localizedDescription];
  2363. return;
  2364. }
  2365. [self logSuccess:@"Password reset succeeded."];
  2366. [self showMessagePrompt:@"Password reset succeeded."];
  2367. }];
  2368. }];
  2369. }];
  2370. }];
  2371. }];
  2372. }
  2373. /** @fn checkActionCode
  2374. @brief Performs a "check action code" operation.
  2375. */
  2376. - (void)checkActionCode {
  2377. [self showTextInputPromptWithMessage:@"OOB Code:"
  2378. completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  2379. if (!userPressedOK || !userInput.length) {
  2380. return;
  2381. }
  2382. [self showSpinner:^{
  2383. [[AppManager auth] checkActionCode:userInput completion:^(FIRActionCodeInfo *_Nullable info,
  2384. NSError *_Nullable error) {
  2385. [self hideSpinner:^{
  2386. if (error) {
  2387. [self logFailure:@"Check action code failed" error:error];
  2388. [self showMessagePrompt:error.localizedDescription];
  2389. return;
  2390. }
  2391. [self logSuccess:@"Check action code succeeded."];
  2392. NSString *email = [info dataForKey:FIRActionCodeEmailKey];
  2393. NSString *fromEmail = [info dataForKey:FIRActionCodeFromEmailKey];
  2394. NSString *message =
  2395. fromEmail ? [NSString stringWithFormat:@"%@ -> %@", fromEmail, email] : email;
  2396. NSString *operation = [self nameForActionCodeOperation:info.operation];
  2397. [self showMessagePromptWithTitle:operation
  2398. message:message
  2399. showCancelButton:NO
  2400. completion:nil];
  2401. }];
  2402. }];
  2403. }];
  2404. }];
  2405. }
  2406. /** @fn applyActionCode
  2407. @brief Performs a "apply action code" operation.
  2408. */
  2409. - (void)applyActionCode {
  2410. [self showTextInputPromptWithMessage:@"OOB Code:"
  2411. completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  2412. if (!userPressedOK || !userInput.length) {
  2413. return;
  2414. }
  2415. [self showSpinner:^{
  2416. [[AppManager auth] applyActionCode:userInput completion:^(NSError *_Nullable error) {
  2417. [self hideSpinner:^{
  2418. if (error) {
  2419. [self logFailure:@"Apply action code failed" error:error];
  2420. [self showMessagePrompt:error.localizedDescription];
  2421. return;
  2422. }
  2423. [self logSuccess:@"Apply action code succeeded."];
  2424. [self showMessagePrompt:@"Action code was properly applied."];
  2425. }];
  2426. }];
  2427. }];
  2428. }];
  2429. }
  2430. /** @fn verifyPasswordResetCode
  2431. @brief Performs a "verify password reset code" operation.
  2432. */
  2433. - (void)verifyPasswordResetCode {
  2434. [self showTextInputPromptWithMessage:@"OOB Code:"
  2435. completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  2436. if (!userPressedOK || !userInput.length) {
  2437. return;
  2438. }
  2439. [self showSpinner:^{
  2440. [[AppManager auth] verifyPasswordResetCode:userInput completion:^(NSString *_Nullable email,
  2441. NSError *_Nullable error) {
  2442. [self hideSpinner:^{
  2443. if (error) {
  2444. [self logFailure:@"Verify password reset code failed" error:error];
  2445. [self showMessagePrompt:error.localizedDescription];
  2446. return;
  2447. }
  2448. [self logSuccess:@"Verify password resest code succeeded."];
  2449. NSString *alertMessage =
  2450. [[NSString alloc] initWithFormat:@"Code verified for email: %@", email];
  2451. [self showMessagePrompt:alertMessage];
  2452. }];
  2453. }];
  2454. }];
  2455. }];
  2456. }
  2457. /** @fn nameForActionCodeOperation
  2458. @brief Returns the string value of the provided FIRActionCodeOperation value.
  2459. @param operation the FIRActionCodeOperation value to convert to string.
  2460. @return String conversion of FIRActionCodeOperation value.
  2461. */
  2462. - (NSString *)nameForActionCodeOperation:(FIRActionCodeOperation)operation {
  2463. switch (operation) {
  2464. case FIRActionCodeOperationVerifyEmail:
  2465. return @"Verify Email";
  2466. case FIRActionCodeOperationRecoverEmail:
  2467. return @"Recover Email";
  2468. case FIRActionCodeOperationPasswordReset:
  2469. return @"Password Reset";
  2470. case FIRActionCodeOperationEmailLink:
  2471. return @"Email Sign-In Link";
  2472. case FIRActionCodeOperationUnknown:
  2473. return @"Unknown action";
  2474. }
  2475. }
  2476. /** @fn updateEmail
  2477. @brief Changes the email address of the current user.
  2478. */
  2479. - (void)updateEmail {
  2480. [self showTextInputPromptWithMessage:@"Email Address:"
  2481. completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  2482. if (!userPressedOK || !userInput.length) {
  2483. return;
  2484. }
  2485. [self showSpinner:^{
  2486. [[self user] updateEmail:userInput completion:^(NSError *_Nullable error) {
  2487. if (error) {
  2488. [self logFailure:@"update email failed" error:error];
  2489. } else {
  2490. [self logSuccess:@"update email succeeded."];
  2491. }
  2492. [self hideSpinner:^{
  2493. [self showTypicalUIForUserUpdateResultsWithTitle:kUpdateEmailText error:error];
  2494. }];
  2495. }];
  2496. }];
  2497. }];
  2498. }
  2499. /** @fn updatePassword
  2500. @brief Updates the password of the current user.
  2501. */
  2502. - (void)updatePassword {
  2503. [self showTextInputPromptWithMessage:@"New Password:"
  2504. completionBlock:^(BOOL userPressedOK, NSString *_Nullable userInput) {
  2505. if (!userPressedOK) {
  2506. return;
  2507. }
  2508. [self showSpinner:^{
  2509. [[self user] updatePassword:userInput completion:^(NSError *_Nullable error) {
  2510. if (error) {
  2511. [self logFailure:@"update password failed" error:error];
  2512. } else {
  2513. [self logSuccess:@"update password succeeded."];
  2514. }
  2515. [self hideSpinner:^{
  2516. [self showTypicalUIForUserUpdateResultsWithTitle:kUpdatePasswordText error:error];
  2517. }];
  2518. }];
  2519. }];
  2520. }];
  2521. }
  2522. /** @fn updateToSavedUser
  2523. @brief updates the current user to the saved user.
  2524. */
  2525. - (void)updateToSavedUser {
  2526. if(![AppManager auth].currentUser) {
  2527. NSLog(@"You must be signed in to perform this action");
  2528. return;
  2529. }
  2530. if (!_userInMemory) {
  2531. NSLog(@"You need an in memory user to perform this action");
  2532. return;
  2533. }
  2534. [[AppManager auth] updateCurrentUser:_userInMemory completion:^(NSError *_Nullable error) {
  2535. if (error) {
  2536. [self showMessagePrompt:
  2537. [NSString stringWithFormat:@"An error Occurred: %@", error.localizedDescription]];
  2538. return;
  2539. }
  2540. }];
  2541. }
  2542. /** @fn createUser
  2543. @brief Creates a new user.
  2544. */
  2545. - (void)createUser {
  2546. [self showTextInputPromptWithMessage:@"Email:"
  2547. keyboardType:UIKeyboardTypeEmailAddress
  2548. completionBlock:^(BOOL userPressedOK, NSString *_Nullable email) {
  2549. if (!userPressedOK || !email.length) {
  2550. return;
  2551. }
  2552. [self showTextInputPromptWithMessage:@"Password:"
  2553. completionBlock:^(BOOL userPressedOK, NSString *_Nullable password) {
  2554. if (!userPressedOK) {
  2555. return;
  2556. }
  2557. [self showSpinner:^{
  2558. [[AppManager auth] createUserWithEmail:email
  2559. password:password
  2560. completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  2561. if (error) {
  2562. [self logFailure:@"create user failed" error:error];
  2563. } else {
  2564. [self logSuccess:@"create user succeeded."];
  2565. }
  2566. [self hideSpinner:^{
  2567. [self showTypicalUIForUserUpdateResultsWithTitle:kCreateUserTitle error:error];
  2568. }];
  2569. }];
  2570. }];
  2571. }];
  2572. }];
  2573. }
  2574. /** @fn createUserAuthDataResult
  2575. @brief Creates a new user.
  2576. */
  2577. - (void)createUserAuthDataResult {
  2578. [self showTextInputPromptWithMessage:@"Email:"
  2579. keyboardType:UIKeyboardTypeEmailAddress
  2580. completionBlock:^(BOOL userPressedOK, NSString *_Nullable email) {
  2581. if (!userPressedOK || !email.length) {
  2582. return;
  2583. }
  2584. [self showTextInputPromptWithMessage:@"Password:"
  2585. completionBlock:^(BOOL userPressedOK, NSString *_Nullable password) {
  2586. if (!userPressedOK) {
  2587. return;
  2588. }
  2589. [self showSpinner:^{
  2590. [[AppManager auth] createUserAndRetrieveDataWithEmail:email
  2591. password:password
  2592. completion:^(FIRAuthDataResult *_Nullable result,
  2593. NSError *_Nullable error) {
  2594. if (error) {
  2595. [self logFailure:@"create user failed" error:error];
  2596. } else {
  2597. [self logSuccess:@"create user succeeded."];
  2598. [self log:result.user.uid];
  2599. }
  2600. [self hideSpinner:^{
  2601. [self showTypicalUIForUserUpdateResultsWithTitle:kCreateUserTitle error:error];
  2602. }];
  2603. }];
  2604. }];
  2605. }];
  2606. }];
  2607. }
  2608. /** @fn signInWithPhoneNumber
  2609. @brief Allows sign in with phone number.
  2610. */
  2611. - (void)signInWithPhoneNumber {
  2612. [self commonPhoneNumberInputWithTitle:@"Phone #" Completion:^(NSString *_Nullable phone) {
  2613. [self showSpinner:^{
  2614. #pragma clang diagnostic push
  2615. #pragma clang diagnostic ignored "-Wdeprecated-declarations"
  2616. [[AppManager phoneAuthProvider] verifyPhoneNumber:phone
  2617. completion:^(NSString *_Nullable verificationID,
  2618. NSError *_Nullable error) {
  2619. #pragma clang diagnostic pop
  2620. [self hideSpinner:^{
  2621. if (error) {
  2622. [self logFailure:@"failed to send verification code" error:error];
  2623. [self showMessagePrompt:error.localizedDescription];
  2624. return;
  2625. }
  2626. [self logSuccess:@"Code sent"];
  2627. [self commonPhoneNumberInputWithTitle:@"Code"
  2628. Completion:^(NSString *_Nullable verificationCode) {
  2629. [self commontPhoneVerificationWithVerificationID:verificationID
  2630. verificationCode:verificationCode];
  2631. }];
  2632. }];
  2633. }];
  2634. }];
  2635. }];
  2636. }
  2637. /** @fn signInWithPhoneNumber
  2638. @brief Allows sign in with phone number using reCAPTCHA.
  2639. @param phoneNumber Number pass in string.
  2640. @completion A completion block to be executed after successful phone number sign in.
  2641. */
  2642. - (void)signInWithPhoneNumber:(NSString *_Nullable)phoneNumber
  2643. completion:(nullable testAutomationCallback)completion {
  2644. [self showSpinner:^{
  2645. [[AppManager phoneAuthProvider] verifyPhoneNumber:phoneNumber
  2646. UIDelegate:nil
  2647. completion:^(NSString *_Nullable verificationID,
  2648. NSError *_Nullable error) {
  2649. [self hideSpinner:^{
  2650. if (error) {
  2651. [self logFailure:@"failed to send verification code" error:error];
  2652. [self showMessagePrompt:error.localizedDescription];
  2653. if (completion) {
  2654. completion(error);
  2655. }
  2656. return;
  2657. }
  2658. [self logSuccess:@"Code sent"];
  2659. [self commonPhoneNumberInputWithTitle:@"Code"
  2660. Completion:^(NSString *_Nullable verificationCode) {
  2661. [self commontPhoneVerificationWithVerificationID:verificationID
  2662. verificationCode:verificationCode];
  2663. if (completion) {
  2664. completion(nil);
  2665. }
  2666. }];
  2667. }];
  2668. }];
  2669. }];
  2670. }
  2671. /** @fn signInWithPhoneNumberWithPrompt
  2672. @brief Allows sign in with phone number via popup prompt.
  2673. */
  2674. - (void)signInWithPhoneNumberWithPrompt {
  2675. [self commonPhoneNumberInputWithTitle:@"Phone #"
  2676. Completion:^(NSString *_Nullable phone) {
  2677. [self signInWithPhoneNumber:phone completion:nil];
  2678. }];
  2679. }
  2680. /** @fn commonPhoneNumberInputWithLabel:Completion
  2681. @brief Allows user input into a text field.
  2682. @param title of the promt.
  2683. */
  2684. - (void)commonPhoneNumberInputWithTitle:(NSString *)title
  2685. Completion:(textInputCompletionBlock)completion {
  2686. [self showTextInputPromptWithMessage:title
  2687. keyboardType:UIKeyboardTypePhonePad
  2688. completionBlock:^(BOOL userPressedOK, NSString *_Nullable phoneNumber) {
  2689. if (!userPressedOK) {
  2690. return;
  2691. }
  2692. completion(phoneNumber);
  2693. }];
  2694. }
  2695. /** @fn commonPhoneNumberInputWithLabel:Completion
  2696. @brief Finishes the phone number verification flow.
  2697. @param verificationID The verificationID from the backend.
  2698. @param verificationCode The verificationCode from the SMS message.
  2699. */
  2700. - (void)commontPhoneVerificationWithVerificationID:(NSString *)verificationID
  2701. verificationCode:(NSString *)verificationCode {
  2702. [self showSpinner:^{
  2703. FIRAuthCredential *credential =
  2704. [[AppManager phoneAuthProvider] credentialWithVerificationID:verificationID
  2705. verificationCode:verificationCode];
  2706. [[AppManager auth] signInAndRetrieveDataWithCredential:credential
  2707. completion:^(FIRAuthDataResult *_Nullable result,
  2708. NSError *_Nullable error) {
  2709. [self hideSpinner:^{
  2710. if (error) {
  2711. [self logFailure:@"failed to verify phone number" error:error];
  2712. [self showMessagePrompt:error.localizedDescription];
  2713. return;
  2714. }
  2715. if (_isNewUserToggleOn) {
  2716. NSString *newUserString = result.additionalUserInfo.isNewUser ?
  2717. @"New user" : @"Existing user";
  2718. [self showMessagePromptWithTitle:@"New or Existing"
  2719. message:newUserString
  2720. showCancelButton:NO
  2721. completion:nil];
  2722. }
  2723. }];
  2724. }];
  2725. }];
  2726. }
  2727. /** @fn updatePhoneNumber
  2728. @brief Allows adding a verified phone number to the currently signed user.
  2729. @param phoneNumber Number pass in string.
  2730. @completion A completion block to be executed after phone number is updated.
  2731. */
  2732. - (void)updatePhoneNumber:(NSString *_Nullable)phoneNumber
  2733. completion:(nullable testAutomationCallback)completion {
  2734. [self showSpinner:^{
  2735. [[AppManager phoneAuthProvider] verifyPhoneNumber:phoneNumber
  2736. UIDelegate:nil
  2737. completion:^(NSString *_Nullable verificationID,
  2738. NSError *_Nullable error) {
  2739. if (error) {
  2740. [self logFailure:@"failed to send verification code" error:error];
  2741. [self showMessagePrompt:error.localizedDescription];
  2742. if (completion) {
  2743. completion(error);
  2744. }
  2745. return;
  2746. }
  2747. [self logSuccess:@"Code sent"];
  2748. [self showTextInputPromptWithMessage:@"Verification code:"
  2749. keyboardType:UIKeyboardTypeNumberPad
  2750. completionBlock:^(BOOL userPressedOK,
  2751. NSString *_Nullable verificationCode) {
  2752. if (!userPressedOK || !verificationCode.length) {
  2753. return;
  2754. }
  2755. [self showSpinner:^{
  2756. FIRPhoneAuthCredential *credential =
  2757. [[AppManager phoneAuthProvider] credentialWithVerificationID:verificationID
  2758. verificationCode:verificationCode];
  2759. [[self user] updatePhoneNumberCredential:credential
  2760. completion:^(NSError *_Nullable error) {
  2761. if (error) {
  2762. [self logFailure:@"update phone number failed" error:error];
  2763. [self showMessagePrompt:error.localizedDescription];
  2764. if (completion) {
  2765. completion(error);
  2766. }
  2767. } else {
  2768. [self logSuccess:@"update phone number succeeded."];
  2769. if (completion) {
  2770. completion(nil);
  2771. }
  2772. }
  2773. }];
  2774. }];
  2775. }];
  2776. [self hideSpinner:^{
  2777. [self showTypicalUIForUserUpdateResultsWithTitle:kCreateUserTitle error:error];
  2778. }];
  2779. }];
  2780. }];
  2781. }
  2782. /** @fn updatePhoneNumberWithPrompt
  2783. @brief Allows adding a verified phone number to the currently signed user via popup prompt.
  2784. */
  2785. - (void)updatePhoneNumberWithPrompt {
  2786. [self showTextInputPromptWithMessage:@"Update Phone #:"
  2787. keyboardType:UIKeyboardTypePhonePad
  2788. completionBlock:^(BOOL userPressedOK, NSString *_Nullable phoneNumber) {
  2789. if (!userPressedOK || !phoneNumber.length) {
  2790. return;
  2791. }
  2792. [self updatePhoneNumber:phoneNumber completion:nil];
  2793. }];
  2794. }
  2795. /** @fn linkPhoneNumber
  2796. @brief Allows linking a verified phone number to the currently signed user.
  2797. @param phoneNumber Number pass in string.
  2798. @completion A completion block to be executed after linking phone number.
  2799. */
  2800. - (void)linkPhoneNumber:(NSString *_Nullable)phoneNumber
  2801. completion:(nullable testAutomationCallback)completion {
  2802. [self showSpinner:^{
  2803. [[AppManager phoneAuthProvider] verifyPhoneNumber:phoneNumber
  2804. UIDelegate:nil
  2805. completion:^(NSString *_Nullable verificationID,
  2806. NSError *_Nullable error) {
  2807. [self hideSpinner:^{
  2808. if (error) {
  2809. [self logFailure:@"failed to send verification code" error:error];
  2810. [self showMessagePrompt:error.localizedDescription];
  2811. if (completion) {
  2812. completion(error);
  2813. }
  2814. return;
  2815. }
  2816. [self logSuccess:@"Code sent"];
  2817. [self showTextInputPromptWithMessage:@"Verification code:"
  2818. keyboardType:UIKeyboardTypeNumberPad
  2819. completionBlock:^(BOOL userPressedOK,
  2820. NSString *_Nullable verificationCode) {
  2821. if (!userPressedOK || !verificationCode.length) {
  2822. return;
  2823. }
  2824. [self showSpinner:^{
  2825. FIRPhoneAuthCredential *credential =
  2826. [[AppManager phoneAuthProvider] credentialWithVerificationID:verificationID
  2827. verificationCode:verificationCode];
  2828. [[self user] linkWithCredential:credential
  2829. completion:^(FIRUser *_Nullable user,
  2830. NSError *_Nullable error) {
  2831. [self hideSpinner:^{
  2832. if (error) {
  2833. if (error.code == FIRAuthErrorCodeCredentialAlreadyInUse) {
  2834. [self showMessagePromptWithTitle:@"Phone number is already linked to "
  2835. @"another user"
  2836. message:@"Tap Ok to sign in with that user now."
  2837. showCancelButton:YES
  2838. completion:^(BOOL userPressedOK,
  2839. NSString *_Nullable userInput) {
  2840. if (userPressedOK) {
  2841. // If FIRAuthErrorCodeCredentialAlreadyInUse error, sign in with the
  2842. // provided credential.
  2843. [self showSpinner:^{
  2844. FIRPhoneAuthCredential *credential =
  2845. error.userInfo[FIRAuthUpdatedCredentialKey];
  2846. [[AppManager auth] signInWithCredential:credential
  2847. completion:^(FIRUser *_Nullable user,
  2848. NSError *_Nullable error) {
  2849. [self hideSpinner:^{
  2850. if (error) {
  2851. [self logFailure:@"failed to verify phone number" error:error];
  2852. [self showMessagePrompt:error.localizedDescription];
  2853. if (completion) {
  2854. completion(error);
  2855. }
  2856. return;
  2857. }
  2858. }];
  2859. }];
  2860. }];
  2861. }
  2862. }];
  2863. } else {
  2864. [self logFailure:@"link phone number failed" error:error];
  2865. [self showMessagePrompt:error.localizedDescription];
  2866. }
  2867. return;
  2868. }
  2869. [self logSuccess:@"link phone number succeeded."];
  2870. if (completion) {
  2871. completion(nil);
  2872. }
  2873. }];
  2874. }];
  2875. }];
  2876. }];
  2877. }];
  2878. }];
  2879. }];
  2880. }
  2881. /** @fn linkPhoneNumberWithPrompt
  2882. @brief Allows linking a verified phone number to the currently signed user via popup prompt.
  2883. */
  2884. - (void)linkPhoneNumberWithPrompt {
  2885. [self showTextInputPromptWithMessage:@"Phone #:"
  2886. keyboardType:UIKeyboardTypePhonePad
  2887. completionBlock:^(BOOL userPressedOK, NSString *_Nullable phoneNumber) {
  2888. if (!userPressedOK || !phoneNumber.length) {
  2889. return;
  2890. }
  2891. [self linkPhoneNumber:phoneNumber completion:nil];
  2892. }];
  2893. }
  2894. /** @fn signInAnonymously
  2895. @brief Signs in as an anonymous user.
  2896. */
  2897. - (void)signInAnonymously {
  2898. [[AppManager auth] signInAnonymouslyWithCompletion:^(FIRUser *_Nullable user,
  2899. NSError *_Nullable error) {
  2900. if (error) {
  2901. [self logFailure:@"sign-in anonymously failed" error:error];
  2902. } else {
  2903. [self logSuccess:@"sign-in anonymously succeeded."];
  2904. }
  2905. [self showTypicalUIForUserUpdateResultsWithTitle:kSignInAnonymouslyButtonText error:error];
  2906. }];
  2907. }
  2908. /** @fn signInAnonymouslyAuthDataResult
  2909. @brief Signs in as an anonymous user, receiving an auth result containing a signed in user upon
  2910. success.
  2911. */
  2912. - (void)signInAnonymouslyAuthDataResult {
  2913. [[AppManager auth] signInAnonymouslyAndRetrieveDataWithCompletion:
  2914. ^(FIRAuthDataResult *_Nullable authResult, NSError *_Nullable error) {
  2915. if (error) {
  2916. [self logFailure:@"sign-in anonymously failed" error:error];
  2917. } else {
  2918. [self logSuccess:@"sign-in anonymously succeeded."];
  2919. [self log:[NSString stringWithFormat:@"User ID : %@", authResult.user.uid]];
  2920. }
  2921. [self showTypicalUIForUserUpdateResultsWithTitle:kSignInAnonymouslyButtonText error:error];
  2922. }];
  2923. }
  2924. /** @fn signInWithGitHub
  2925. @brief Signs in as a GitHub user. Prompts the user for an access token and uses this access
  2926. token to create a GitHub (generic) credential for signing in.
  2927. */
  2928. - (void)signInWithGitHub {
  2929. [self showTextInputPromptWithMessage:@"GitHub Access Token:"
  2930. completionBlock:^(BOOL userPressedOK, NSString *_Nullable accessToken) {
  2931. if (!userPressedOK || !accessToken.length) {
  2932. return;
  2933. }
  2934. FIRAuthCredential *credential =
  2935. [FIROAuthProvider credentialWithProviderID:FIRGitHubAuthProviderID accessToken:accessToken];
  2936. if (credential) {
  2937. [[AppManager auth] signInWithCredential:credential completion:^(FIRUser *_Nullable user,
  2938. NSError *_Nullable error) {
  2939. if (error) {
  2940. [self logFailure:@"sign-in with provider failed" error:error];
  2941. } else {
  2942. [self logSuccess:@"sign-in with provider succeeded."];
  2943. }
  2944. [self showTypicalUIForUserUpdateResultsWithTitle:@"Sign-In" error:error];
  2945. }];
  2946. }
  2947. }];
  2948. }
  2949. /** @fn deleteApp
  2950. @brief Deletes the @c FIRApp associated with our @c FIRAuth instance.
  2951. */
  2952. - (void)deleteApp {
  2953. [[FIRApp defaultApp] deleteApp:^(BOOL success) {
  2954. [self log:success ? @"App deleted successfully." : @"Failed to delete app."];
  2955. }];
  2956. }
  2957. /** @fn timeAuthInitialization
  2958. @brief Times FIRAuth instance initialization time.
  2959. */
  2960. - (void)timeAuthInitialization {
  2961. // Temporarily disable auth state listener to avoid interfering with the result.
  2962. [[NSNotificationCenter defaultCenter] removeObserver:self
  2963. name:FIRAuthStateDidChangeNotification
  2964. object:nil];
  2965. [self showSpinner:^() {
  2966. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^() {
  2967. const int numberOfRuns = 4096;
  2968. FIRApp *app = [FIRApp defaultApp];
  2969. NSString *key = NSStringFromClass([FIRAuth class]);
  2970. NSDate *startTime = [NSDate date];
  2971. for (int i = 0; i < numberOfRuns; i++) {
  2972. @autoreleasepool {
  2973. [FIRAppAssociationRegistration deregisterObjectWithHost:app key:key];
  2974. [FIRAuth auth];
  2975. }
  2976. }
  2977. NSDate *endTime = [NSDate date];
  2978. dispatch_async(dispatch_get_main_queue(), ^() {
  2979. // Re-enable auth state listener.
  2980. [[NSNotificationCenter defaultCenter] addObserver:self
  2981. selector:@selector(authStateChangedForAuth:)
  2982. name:FIRAuthStateDidChangeNotification
  2983. object:nil];
  2984. [self hideSpinner:^() {
  2985. NSTimeInterval averageTime = [endTime timeIntervalSinceDate:startTime] / numberOfRuns;
  2986. NSString *message = [NSString stringWithFormat:
  2987. @"Each [FIRAuth auth] takes average of %.3f ms for %d runs",
  2988. averageTime * 1000, numberOfRuns];
  2989. [self showMessagePromptWithTitle:@"Timing Result"
  2990. message:message
  2991. showCancelButton:NO
  2992. completion:nil];
  2993. }];
  2994. });
  2995. });
  2996. }];
  2997. }
  2998. #pragma mark - Helpers
  2999. /** @fn user
  3000. @brief The user to use for user operations. Takes into account the "use signed-in user vs. use
  3001. user in memory" setting.
  3002. */
  3003. - (FIRUser *)user {
  3004. return _useUserInMemory ? _userInMemory : [AppManager auth].currentUser;
  3005. }
  3006. /** @fn actionCodeSettings
  3007. @brief Returns the action code settings for this app.
  3008. */
  3009. - (FIRActionCodeSettings *)actionCodeSettings {
  3010. FIRActionCodeSettings *actionCodeSettings = [[FIRActionCodeSettings alloc] init];
  3011. actionCodeSettings.URL = _actionCodeContinueURL;
  3012. actionCodeSettings.handleCodeInApp = _actionCodeRequestType == ActionCodeRequestTypeInApp;
  3013. return actionCodeSettings;
  3014. }
  3015. /** @fn stringWithAdditionalUserInfo:
  3016. @brief Gets the string description of the additional user info.
  3017. @param additionalUserInfo The additional user info in question.
  3018. @return A string to description the additional user info.
  3019. */
  3020. - (NSString *)stringWithAdditionalUserInfo:(nullable FIRAdditionalUserInfo *)additionalUserInfo {
  3021. if (!additionalUserInfo) {
  3022. return @"(no additional user info)";
  3023. }
  3024. NSString *newUserString = additionalUserInfo.isNewUser ? @"new user" : @"existing user";
  3025. return [NSString stringWithFormat:@"%@: %@", newUserString, additionalUserInfo.profile];
  3026. }
  3027. /** @fn showTypicalUIForUserUpdateResultsWithTitle:error:
  3028. @brief Shows a prompt if error is non-nil with the localized description of the error.
  3029. @param resultsTitle The title of the prompt
  3030. @param error The error details to display if non-nil.
  3031. */
  3032. - (void)showTypicalUIForUserUpdateResultsWithTitle:(NSString *)resultsTitle
  3033. error:(NSError *)error {
  3034. if (error) {
  3035. NSString *message = [NSString stringWithFormat:@"%@ (%ld)\n%@",
  3036. error.domain,
  3037. (long)error.code,
  3038. error.localizedDescription];
  3039. if (error.code == FIRAuthErrorCodeAccountExistsWithDifferentCredential) {
  3040. NSString *errorEmail = error.userInfo[FIRAuthErrorUserInfoEmailKey];
  3041. resultsTitle = [NSString stringWithFormat:@"Existing email : %@", errorEmail];
  3042. }
  3043. [self showMessagePromptWithTitle:resultsTitle
  3044. message:message
  3045. showCancelButton:NO
  3046. completion:nil];
  3047. return;
  3048. }
  3049. [self updateUserInfo];
  3050. }
  3051. /** @fn showUIForAuthDataResultWithResult:error:
  3052. @brief Shows a prompt if error is non-nil with the localized description of the error.
  3053. @param result The auth data result if non-nil.
  3054. @param error The error details to display if non-nil.
  3055. */
  3056. - (void)showUIForAuthDataResultWithResult:(FIRAuthDataResult *)result
  3057. error:(NSError *)error {
  3058. NSString *errorMessage = [NSString stringWithFormat:@"%@ (%ld)\n%@",
  3059. error.domain ?: @"",
  3060. (long)error.code,
  3061. error.localizedDescription ?: @""];
  3062. [self showMessagePromptWithTitle:@"Error"
  3063. message:errorMessage
  3064. showCancelButton:NO
  3065. completion:^(BOOL userPressedOK,
  3066. NSString *_Nullable userInput) {
  3067. [self showMessagePromptWithTitle:@"Profile Info"
  3068. message:[self stringWithAdditionalUserInfo:result.additionalUserInfo]
  3069. showCancelButton:NO
  3070. completion:nil];
  3071. [self updateUserInfo];
  3072. }];
  3073. }
  3074. - (void)doSignInWithCustomToken:(NSString *_Nullable)userEnteredTokenText {
  3075. [[AppManager auth] signInWithCustomToken:userEnteredTokenText
  3076. completion:^(FIRUser *_Nullable user, NSError *_Nullable error) {
  3077. if (error) {
  3078. [self logFailure:@"sign-in with custom token failed" error:error];
  3079. [self showMessagePromptWithTitle:kSignInErrorAlertTitle
  3080. message:error.localizedDescription
  3081. showCancelButton:NO
  3082. completion:nil];
  3083. return;
  3084. }
  3085. [self logSuccess:@"sign-in with custom token succeeded."];
  3086. [self showMessagePromptWithTitle:kSignedInAlertTitle
  3087. message:user.displayName
  3088. showCancelButton:NO
  3089. completion:nil];
  3090. }];
  3091. }
  3092. - (void)doSignInAndRetrieveDataWithCustomToken:(NSString *_Nullable)userEnteredTokenText {
  3093. [[AppManager auth] signInAndRetrieveDataWithCustomToken:userEnteredTokenText
  3094. completion:^(FIRAuthDataResult *_Nullable result,
  3095. NSError *_Nullable error) {
  3096. if (error) {
  3097. [self logFailure:@"sign-in with custom token failed" error:error];
  3098. [self showMessagePromptWithTitle:kSignInErrorAlertTitle
  3099. message:error.localizedDescription
  3100. showCancelButton:NO
  3101. completion:nil];
  3102. return;
  3103. }
  3104. [self logSuccess:@"sign-in with custom token succeeded."];
  3105. [self showMessagePromptWithTitle:kSignedInAlertTitle
  3106. message:result.user.displayName
  3107. showCancelButton:NO
  3108. completion:nil];
  3109. }];
  3110. }
  3111. - (void)updateUserInfo {
  3112. [_userInfoTableViewCell updateContentsWithUser:[AppManager auth].currentUser];
  3113. [_userInMemoryInfoTableViewCell updateContentsWithUser:_userInMemory];
  3114. }
  3115. - (void)authStateChangedForAuth:(NSNotification *)notification {
  3116. [self updateUserInfo];
  3117. if (notification) {
  3118. [self log:[NSString stringWithFormat:
  3119. @"received FIRAuthStateDidChange notification on user '%@'.",
  3120. ((FIRAuth *)notification.object).currentUser.uid]];
  3121. }
  3122. }
  3123. /** @fn clearConsole
  3124. @brief Clears the console text.
  3125. */
  3126. - (IBAction)clearConsole:(id)sender {
  3127. [_consoleString appendString:@"\n\n"];
  3128. _consoleTextView.text = @"";
  3129. }
  3130. /** @fn copyConsole
  3131. @brief Copies the current console string to the clipboard.
  3132. */
  3133. - (IBAction)copyConsole:(id)sender {
  3134. UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
  3135. pasteboard.string = _consoleString ?: @"";
  3136. }
  3137. @end
  3138. NS_ASSUME_NONNULL_END