AuthMenu.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  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. case passkeySignUp
  55. case passkeySignIn
  56. case passkeyEnroll
  57. case passkeyUnenroll
  58. // More intuitively named getter for `rawValue`.
  59. var id: String { rawValue }
  60. // The UI friendly name of the `AuthMenu`. Used for display.
  61. var name: String {
  62. switch self {
  63. case .settings:
  64. return "Settings"
  65. case .google:
  66. return "Google"
  67. case .apple:
  68. return "Apple"
  69. case .twitter:
  70. return "Twitter"
  71. case .microsoft:
  72. return "Microsoft"
  73. case .gitHub:
  74. return "GitHub"
  75. case .yahoo:
  76. return "Yahoo"
  77. case .linkedIn:
  78. return "LinkedIn"
  79. case .facebook:
  80. return "Facebook"
  81. case .gameCenter:
  82. return "Game Center"
  83. case .emailPassword:
  84. return "Email & Password Login"
  85. case .passwordless:
  86. return "Email Link/Passwordless"
  87. case .phoneNumber:
  88. return "Phone Number"
  89. case .anonymous:
  90. return "Anonymous Authentication"
  91. case .custom:
  92. return "Custom Auth System"
  93. // Recpatcha
  94. case .initRecaptcha:
  95. return "Initialize reCAPTCHA Enterprise"
  96. // Custom Auth Domain
  97. case .customAuthDomain:
  98. return "Set Custom Auth Domain"
  99. // App Section
  100. case .getToken:
  101. return "Get Token"
  102. case .getTokenForceRefresh:
  103. return "Get Token Force Refresh"
  104. case .addAuthStateChangeListener:
  105. return "Add Auth State Change Listener"
  106. case .removeLastAuthStateChangeListener:
  107. return "Remove Last Auth State Change Listener"
  108. case .addIdTokenChangeListener:
  109. return "Add ID Token Change Listener"
  110. case .removeLastIdTokenChangeListener:
  111. return "Remove Last ID Token Change Listener"
  112. case .verifyClient:
  113. return "Verify Client"
  114. case .deleteApp:
  115. return "Delete App"
  116. // OOB
  117. case .actionType:
  118. return "Action Type"
  119. case .continueURL:
  120. return "Continue URL"
  121. case .linkDomain:
  122. return "Link Domain"
  123. case .requestVerifyEmail:
  124. return "Request Verify Email"
  125. case .requestPasswordReset:
  126. return "Request Password Reset"
  127. case .resetPassword:
  128. return "Reset Password"
  129. case .checkActionCode:
  130. return "Check Action Code"
  131. case .applyActionCode:
  132. return "Apply Action Code"
  133. case .verifyPasswordResetCode:
  134. return "Verify Password Reset Code"
  135. // Multi factor
  136. case .phoneEnroll:
  137. return "Phone Enroll"
  138. case .totpEnroll:
  139. return "TOTP Enroll"
  140. case .multifactorUnenroll:
  141. return "Multifactor unenroll"
  142. // Passkey
  143. case .passkeySignUp:
  144. return "Sign Up with Passkey"
  145. case .passkeySignIn:
  146. return "Sign In with Passkey"
  147. case .passkeyEnroll:
  148. return "Enroll with Passkey"
  149. case .passkeyUnenroll:
  150. return "Unenroll Passkey"
  151. }
  152. }
  153. // Failable initializer to create an `AuthMenu` from its corresponding `name` value.
  154. // - Parameter rawValue: String value representing `AuthMenu`'s name or type.
  155. init?(rawValue: String) {
  156. switch rawValue {
  157. case "Settings":
  158. self = .settings
  159. case "Google":
  160. self = .google
  161. case "Apple":
  162. self = .apple
  163. case "Twitter":
  164. self = .twitter
  165. case "Microsoft":
  166. self = .microsoft
  167. case "GitHub":
  168. self = .gitHub
  169. case "Yahoo":
  170. self = .yahoo
  171. case "LinkedIn":
  172. self = .linkedIn
  173. case "Facebook":
  174. self = .facebook
  175. case "Game Center":
  176. self = .gameCenter
  177. case "Email & Password Login":
  178. self = .emailPassword
  179. case "Email Link/Passwordless":
  180. self = .passwordless
  181. case "Phone Number":
  182. self = .phoneNumber
  183. case "Anonymous Authentication":
  184. self = .anonymous
  185. case "Custom Auth System":
  186. self = .custom
  187. case "Initialize reCAPTCHA Enterprise":
  188. self = .initRecaptcha
  189. case "Set Custom Auth Domain":
  190. self = .customAuthDomain
  191. case "Get Token":
  192. self = .getToken
  193. case "Get Token Force Refresh":
  194. self = .getTokenForceRefresh
  195. case "Add Auth State Change Listener":
  196. self = .addAuthStateChangeListener
  197. case "Remove Last Auth State Change Listener":
  198. self = .removeLastAuthStateChangeListener
  199. case "Add ID Token Change Listener":
  200. self = .addIdTokenChangeListener
  201. case "Remove Last ID Token Change Listener":
  202. self = .removeLastIdTokenChangeListener
  203. case "Verify Client":
  204. self = .verifyClient
  205. case "Delete App":
  206. self = .deleteApp
  207. case "Action Type":
  208. self = .actionType
  209. case "Continue URL":
  210. self = .continueURL
  211. case "Link Domain":
  212. self = .linkDomain
  213. case "Request Verify Email":
  214. self = .requestVerifyEmail
  215. case "Request Password Reset":
  216. self = .requestPasswordReset
  217. case "Reset Password":
  218. self = .resetPassword
  219. case "Check Action Code":
  220. self = .checkActionCode
  221. case "Apply Action Code":
  222. self = .applyActionCode
  223. case "Verify Password Reset Code":
  224. self = .verifyPasswordResetCode
  225. case "Phone Enroll":
  226. self = .phoneEnroll
  227. case "TOTP Enroll":
  228. self = .totpEnroll
  229. case "Multifactor unenroll":
  230. self = .multifactorUnenroll
  231. case "Sign Up with Passkey":
  232. self = .passkeySignUp
  233. case "Sign In with Passkey":
  234. self = .passkeySignIn
  235. case "Enroll with Passkey":
  236. self = .passkeyEnroll
  237. case "Unenroll Passkey":
  238. self = .passkeyUnenroll
  239. default:
  240. return nil
  241. }
  242. }
  243. }
  244. enum ActionCodeRequestType: String {
  245. case email
  246. case `continue`
  247. case inApp
  248. var name: String {
  249. switch self {
  250. case .email:
  251. return "Email Only"
  252. case .inApp:
  253. return "In-App + Continue URL"
  254. case .continue:
  255. return "Continue URL"
  256. }
  257. }
  258. init?(rawValue: String) {
  259. switch rawValue {
  260. case "Email Only":
  261. self = .email
  262. case "In-App + Continue URL":
  263. self = .inApp
  264. case "Continue URL":
  265. self = .continue
  266. default:
  267. return nil
  268. }
  269. }
  270. }
  271. // MARK: DataSourceProvidable
  272. class AuthMenuData: DataSourceProvidable {
  273. private static var providers: [AuthMenu] {
  274. [.google, .apple, .twitter, .microsoft, .gitHub, .yahoo, .linkedIn, .facebook, .gameCenter]
  275. }
  276. static var settingsSection: Section {
  277. let header = "Auth Settings"
  278. let item = Item(title: AuthMenu.settings.name, hasNestedContent: true)
  279. return Section(headerDescription: header, items: [item])
  280. }
  281. static var providerSection: Section {
  282. let providers = self.providers.map { Item(title: $0.name) }
  283. let header = "Identity Providers"
  284. let footer = "Choose a login flow from one of the identity providers above."
  285. return Section(headerDescription: header, footerDescription: footer, items: providers)
  286. }
  287. static var emailPasswordSection: Section {
  288. let image = UIImage(named: "firebaseIcon")
  289. let header = "Email and Password Login"
  290. let item = Item(title: AuthMenu.emailPassword.name, hasNestedContent: true, image: image)
  291. return Section(headerDescription: header, items: [item])
  292. }
  293. static var otherSection: Section {
  294. let lockSymbol = UIImage.systemImage("lock.slash.fill", tintColor: .systemOrange)
  295. let phoneSymbol = UIImage.systemImage("phone.fill", tintColor: .systemOrange)
  296. let anonSymbol = UIImage.systemImage("questionmark.circle.fill", tintColor: .systemOrange)
  297. let shieldSymbol = UIImage.systemImage("lock.shield.fill", tintColor: .systemOrange)
  298. let otherOptions = [
  299. Item(title: AuthMenu.passwordless.name, image: lockSymbol),
  300. Item(title: AuthMenu.phoneNumber.name, image: phoneSymbol),
  301. Item(title: AuthMenu.anonymous.name, image: anonSymbol),
  302. Item(title: AuthMenu.custom.name, image: shieldSymbol),
  303. ]
  304. let header = "Other Authentication Methods"
  305. return Section(headerDescription: header, items: otherOptions)
  306. }
  307. static var recaptchaSection: Section {
  308. let image = UIImage(named: "firebaseIcon")
  309. let header = "Initialize reCAPTCHA Enterprise"
  310. let item = Item(title: AuthMenu.initRecaptcha.name, hasNestedContent: false, image: image)
  311. return Section(headerDescription: header, items: [item])
  312. }
  313. static var customAuthDomainSection: Section {
  314. let image = UIImage(named: "firebaseIcon")
  315. let header = "Custom Auth Domain"
  316. let item = Item(title: AuthMenu.customAuthDomain.name, hasNestedContent: false, image: image)
  317. return Section(headerDescription: header, items: [item])
  318. }
  319. static var appSection: Section {
  320. let header = "APP"
  321. let items: [Item] = [
  322. Item(title: AuthMenu.getToken.name),
  323. Item(title: AuthMenu.getTokenForceRefresh.name),
  324. Item(title: AuthMenu.addAuthStateChangeListener.name),
  325. Item(title: AuthMenu.removeLastAuthStateChangeListener.name),
  326. Item(title: AuthMenu.addIdTokenChangeListener.name),
  327. Item(title: AuthMenu.removeLastIdTokenChangeListener.name),
  328. Item(title: AuthMenu.verifyClient.name),
  329. Item(title: AuthMenu.deleteApp.name),
  330. ]
  331. return Section(headerDescription: header, items: items)
  332. }
  333. static var oobSection: Section {
  334. let header = "OOB"
  335. let items: [Item] = [
  336. Item(title: AuthMenu.actionType.name, detailTitle: ActionCodeRequestType.inApp.name),
  337. Item(title: AuthMenu.continueURL.name, detailTitle: "--", isEditable: true),
  338. Item(title: AuthMenu.linkDomain.name, detailTitle: "--", isEditable: true),
  339. Item(title: AuthMenu.requestVerifyEmail.name),
  340. Item(title: AuthMenu.requestPasswordReset.name),
  341. Item(title: AuthMenu.resetPassword.name),
  342. Item(title: AuthMenu.checkActionCode.name),
  343. Item(title: AuthMenu.applyActionCode.name),
  344. Item(title: AuthMenu.verifyPasswordResetCode.name),
  345. ]
  346. return Section(headerDescription: header, items: items)
  347. }
  348. static var multifactorSection: Section {
  349. let header = "Multi Factor"
  350. let items: [Item] = [
  351. Item(title: AuthMenu.phoneEnroll.name),
  352. Item(title: AuthMenu.totpEnroll.name),
  353. Item(title: AuthMenu.multifactorUnenroll.name),
  354. ]
  355. return Section(headerDescription: header, items: items)
  356. }
  357. static var passkeySection: Section {
  358. let header = "Passkey"
  359. let items: [Item] = [
  360. Item(title: AuthMenu.passkeySignUp.name),
  361. Item(title: AuthMenu.passkeySignIn.name),
  362. Item(title: AuthMenu.passkeyEnroll.name),
  363. Item(title: AuthMenu.passkeyUnenroll.name),
  364. ]
  365. return Section(headerDescription: header, items: items)
  366. }
  367. static let sections: [Section] =
  368. [settingsSection, providerSection, emailPasswordSection, otherSection, recaptchaSection,
  369. customAuthDomainSection, appSection, oobSection, multifactorSection, passkeySection]
  370. static var authLinkSections: [Section] {
  371. let allItems = [providerSection, emailPasswordSection, otherSection].flatMap { $0.items }
  372. let header = "Manage linking between providers"
  373. let footer =
  374. "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."
  375. return [Section(headerDescription: header, footerDescription: footer, items: allItems)]
  376. }
  377. var sections: [Section] = AuthMenuData.sections
  378. }