AuthMenu.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. // Copyright 2020 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import UIKit
  15. /// Firebase Auth supported identity providers and other methods of authentication
  16. enum AuthMenu: String {
  17. case settings = "Settings"
  18. case google = "google.com"
  19. case apple = "apple.com"
  20. case twitter = "twitter.com"
  21. case microsoft = "microsoft.com"
  22. case gitHub = "github.com"
  23. case yahoo = "yahoo.com"
  24. case linkedIn = "linkedin.com"
  25. case facebook = "facebook.com"
  26. case gameCenter = "gc.apple.com"
  27. case emailPassword = "password"
  28. case passwordless = "emailLink"
  29. case phoneNumber = "phone"
  30. case anonymous
  31. case custom
  32. case initRecaptcha
  33. case customAuthDomain
  34. case getToken
  35. case getTokenForceRefresh
  36. case addAuthStateChangeListener
  37. case removeLastAuthStateChangeListener
  38. case addIdTokenChangeListener
  39. case removeLastIdTokenChangeListener
  40. case verifyClient
  41. case deleteApp
  42. case actionType
  43. case continueURL
  44. case linkDomain
  45. case requestVerifyEmail
  46. case requestPasswordReset
  47. case resetPassword
  48. case checkActionCode
  49. case applyActionCode
  50. case verifyPasswordResetCode
  51. case phoneEnroll
  52. case totpEnroll
  53. case multifactorUnenroll
  54. // More intuitively named getter for `rawValue`.
  55. var id: String { rawValue }
  56. // The UI friendly name of the `AuthMenu`. Used for display.
  57. var name: String {
  58. switch self {
  59. case .settings:
  60. return "Settings"
  61. case .google:
  62. return "Google"
  63. case .apple:
  64. return "Apple"
  65. case .twitter:
  66. return "Twitter"
  67. case .microsoft:
  68. return "Microsoft"
  69. case .gitHub:
  70. return "GitHub"
  71. case .yahoo:
  72. return "Yahoo"
  73. case .linkedIn:
  74. return "LinkedIn"
  75. case .facebook:
  76. return "Facebook"
  77. case .gameCenter:
  78. return "Game Center"
  79. case .emailPassword:
  80. return "Email & Password Login"
  81. case .passwordless:
  82. return "Email Link/Passwordless"
  83. case .phoneNumber:
  84. return "Phone Number"
  85. case .anonymous:
  86. return "Anonymous Authentication"
  87. case .custom:
  88. return "Custom Auth System"
  89. // Recpatcha
  90. case .initRecaptcha:
  91. return "Initialize reCAPTCHA Enterprise"
  92. // Custom Auth Domain
  93. case .customAuthDomain:
  94. return "Set Custom Auth Domain"
  95. // App Section
  96. case .getToken:
  97. return "Get Token"
  98. case .getTokenForceRefresh:
  99. return "Get Token Force Refresh"
  100. case .addAuthStateChangeListener:
  101. return "Add Auth State Change Listener"
  102. case .removeLastAuthStateChangeListener:
  103. return "Remove Last Auth State Change Listener"
  104. case .addIdTokenChangeListener:
  105. return "Add ID Token Change Listener"
  106. case .removeLastIdTokenChangeListener:
  107. return "Remove Last ID Token Change Listener"
  108. case .verifyClient:
  109. return "Verify Client"
  110. case .deleteApp:
  111. return "Delete App"
  112. // OOB
  113. case .actionType:
  114. return "Action Type"
  115. case .continueURL:
  116. return "Continue URL"
  117. case .linkDomain:
  118. return "Link Domain"
  119. case .requestVerifyEmail:
  120. return "Request Verify Email"
  121. case .requestPasswordReset:
  122. return "Request Password Reset"
  123. case .resetPassword:
  124. return "Reset Password"
  125. case .checkActionCode:
  126. return "Check Action Code"
  127. case .applyActionCode:
  128. return "Apply Action Code"
  129. case .verifyPasswordResetCode:
  130. return "Verify Password Reset Code"
  131. // Multi factor
  132. case .phoneEnroll:
  133. return "Phone Enroll"
  134. case .totpEnroll:
  135. return "TOTP Enroll"
  136. case .multifactorUnenroll:
  137. return "Multifactor unenroll"
  138. }
  139. }
  140. // Failable initializer to create an `AuthMenu` from its corresponding `name` value.
  141. // - Parameter rawValue: String value representing `AuthMenu`'s name or type.
  142. init?(rawValue: String) {
  143. switch rawValue {
  144. case "Settings":
  145. self = .settings
  146. case "Google":
  147. self = .google
  148. case "Apple":
  149. self = .apple
  150. case "Twitter":
  151. self = .twitter
  152. case "Microsoft":
  153. self = .microsoft
  154. case "GitHub":
  155. self = .gitHub
  156. case "Yahoo":
  157. self = .yahoo
  158. case "LinkedIn":
  159. self = .linkedIn
  160. case "Facebook":
  161. self = .facebook
  162. case "Game Center":
  163. self = .gameCenter
  164. case "Email & Password Login":
  165. self = .emailPassword
  166. case "Email Link/Passwordless":
  167. self = .passwordless
  168. case "Phone Number":
  169. self = .phoneNumber
  170. case "Anonymous Authentication":
  171. self = .anonymous
  172. case "Custom Auth System":
  173. self = .custom
  174. case "Initialize reCAPTCHA Enterprise":
  175. self = .initRecaptcha
  176. case "Set Custom Auth Domain":
  177. self = .customAuthDomain
  178. case "Get Token":
  179. self = .getToken
  180. case "Get Token Force Refresh":
  181. self = .getTokenForceRefresh
  182. case "Add Auth State Change Listener":
  183. self = .addAuthStateChangeListener
  184. case "Remove Last Auth State Change Listener":
  185. self = .removeLastAuthStateChangeListener
  186. case "Add ID Token Change Listener":
  187. self = .addIdTokenChangeListener
  188. case "Remove Last ID Token Change Listener":
  189. self = .removeLastIdTokenChangeListener
  190. case "Verify Client":
  191. self = .verifyClient
  192. case "Delete App":
  193. self = .deleteApp
  194. case "Action Type":
  195. self = .actionType
  196. case "Continue URL":
  197. self = .continueURL
  198. case "Link Domain":
  199. self = .linkDomain
  200. case "Request Verify Email":
  201. self = .requestVerifyEmail
  202. case "Request Password Reset":
  203. self = .requestPasswordReset
  204. case "Reset Password":
  205. self = .resetPassword
  206. case "Check Action Code":
  207. self = .checkActionCode
  208. case "Apply Action Code":
  209. self = .applyActionCode
  210. case "Verify Password Reset Code":
  211. self = .verifyPasswordResetCode
  212. case "Phone Enroll":
  213. self = .phoneEnroll
  214. case "TOTP Enroll":
  215. self = .totpEnroll
  216. case "Multifactor unenroll":
  217. self = .multifactorUnenroll
  218. default:
  219. return nil
  220. }
  221. }
  222. }
  223. enum ActionCodeRequestType: String {
  224. case email
  225. case `continue`
  226. case inApp
  227. var name: String {
  228. switch self {
  229. case .email:
  230. return "Email Only"
  231. case .inApp:
  232. return "In-App + Continue URL"
  233. case .continue:
  234. return "Continue URL"
  235. }
  236. }
  237. init?(rawValue: String) {
  238. switch rawValue {
  239. case "Email Only":
  240. self = .email
  241. case "In-App + Continue URL":
  242. self = .inApp
  243. case "Continue URL":
  244. self = .continue
  245. default:
  246. return nil
  247. }
  248. }
  249. }
  250. // MARK: DataSourceProvidable
  251. class AuthMenuData: DataSourceProvidable {
  252. private static var providers: [AuthMenu] {
  253. [.google, .apple, .twitter, .microsoft, .gitHub, .yahoo, .linkedIn, .facebook, .gameCenter]
  254. }
  255. static var settingsSection: Section {
  256. let header = "Auth Settings"
  257. let item = Item(title: AuthMenu.settings.name, hasNestedContent: true)
  258. return Section(headerDescription: header, items: [item])
  259. }
  260. static var providerSection: Section {
  261. let providers = self.providers.map { Item(title: $0.name) }
  262. let header = "Identity Providers"
  263. let footer = "Choose a login flow from one of the identity providers above."
  264. return Section(headerDescription: header, footerDescription: footer, items: providers)
  265. }
  266. static var emailPasswordSection: Section {
  267. let image = UIImage(named: "firebaseIcon")
  268. let header = "Email and Password Login"
  269. let item = Item(title: AuthMenu.emailPassword.name, hasNestedContent: true, image: image)
  270. return Section(headerDescription: header, items: [item])
  271. }
  272. static var otherSection: Section {
  273. let lockSymbol = UIImage.systemImage("lock.slash.fill", tintColor: .systemOrange)
  274. let phoneSymbol = UIImage.systemImage("phone.fill", tintColor: .systemOrange)
  275. let anonSymbol = UIImage.systemImage("questionmark.circle.fill", tintColor: .systemOrange)
  276. let shieldSymbol = UIImage.systemImage("lock.shield.fill", tintColor: .systemOrange)
  277. let otherOptions = [
  278. Item(title: AuthMenu.passwordless.name, image: lockSymbol),
  279. Item(title: AuthMenu.phoneNumber.name, image: phoneSymbol),
  280. Item(title: AuthMenu.anonymous.name, image: anonSymbol),
  281. Item(title: AuthMenu.custom.name, image: shieldSymbol),
  282. ]
  283. let header = "Other Authentication Methods"
  284. return Section(headerDescription: header, items: otherOptions)
  285. }
  286. static var recaptchaSection: Section {
  287. let image = UIImage(named: "firebaseIcon")
  288. let header = "Initialize reCAPTCHA Enterprise"
  289. let item = Item(title: AuthMenu.initRecaptcha.name, hasNestedContent: false, image: image)
  290. return Section(headerDescription: header, items: [item])
  291. }
  292. static var customAuthDomainSection: Section {
  293. let image = UIImage(named: "firebaseIcon")
  294. let header = "Custom Auth Domain"
  295. let item = Item(title: AuthMenu.customAuthDomain.name, hasNestedContent: false, image: image)
  296. return Section(headerDescription: header, items: [item])
  297. }
  298. static var appSection: Section {
  299. let header = "APP"
  300. let items: [Item] = [
  301. Item(title: AuthMenu.getToken.name),
  302. Item(title: AuthMenu.getTokenForceRefresh.name),
  303. Item(title: AuthMenu.addAuthStateChangeListener.name),
  304. Item(title: AuthMenu.removeLastAuthStateChangeListener.name),
  305. Item(title: AuthMenu.addIdTokenChangeListener.name),
  306. Item(title: AuthMenu.removeLastIdTokenChangeListener.name),
  307. Item(title: AuthMenu.verifyClient.name),
  308. Item(title: AuthMenu.deleteApp.name),
  309. ]
  310. return Section(headerDescription: header, items: items)
  311. }
  312. static var oobSection: Section {
  313. let header = "OOB"
  314. let items: [Item] = [
  315. Item(title: AuthMenu.actionType.name, detailTitle: ActionCodeRequestType.inApp.name),
  316. Item(title: AuthMenu.continueURL.name, detailTitle: "--", isEditable: true),
  317. Item(title: AuthMenu.linkDomain.name, detailTitle: "--", isEditable: true),
  318. Item(title: AuthMenu.requestVerifyEmail.name),
  319. Item(title: AuthMenu.requestPasswordReset.name),
  320. Item(title: AuthMenu.resetPassword.name),
  321. Item(title: AuthMenu.checkActionCode.name),
  322. Item(title: AuthMenu.applyActionCode.name),
  323. Item(title: AuthMenu.verifyPasswordResetCode.name),
  324. ]
  325. return Section(headerDescription: header, items: items)
  326. }
  327. static var multifactorSection: Section {
  328. let header = "Multi Factor"
  329. let items: [Item] = [
  330. Item(title: AuthMenu.phoneEnroll.name),
  331. Item(title: AuthMenu.totpEnroll.name),
  332. Item(title: AuthMenu.multifactorUnenroll.name),
  333. ]
  334. return Section(headerDescription: header, items: items)
  335. }
  336. static let sections: [Section] =
  337. [settingsSection, providerSection, emailPasswordSection, otherSection, recaptchaSection,
  338. customAuthDomainSection, appSection, oobSection, multifactorSection]
  339. static var authLinkSections: [Section] {
  340. let allItems = [providerSection, emailPasswordSection, otherSection].flatMap { $0.items }
  341. let header = "Manage linking between providers"
  342. let footer =
  343. "Select an unchecked row to link the currently signed in user to that auth provider. To unlink the user from a linked provider, select its corresponding row marked with a checkmark."
  344. return [Section(headerDescription: header, footerDescription: footer, items: allItems)]
  345. }
  346. var sections: [Section] = AuthMenuData.sections
  347. }