GetOOBConfirmationCodeRequest.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. // Copyright 2023 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import Foundation
  15. enum GetOOBConfirmationCodeRequestType: Int {
  16. /** @var FIRGetOOBConfirmationCodeRequestTypePasswordReset
  17. @brief Requests a password reset code.
  18. */
  19. case passwordReset
  20. /** @var FIRGetOOBConfirmationCodeRequestTypeVerifyEmail
  21. @brief Requests an email verification code.
  22. */
  23. case verifyEmail
  24. /** @var FIRGetOOBConfirmationCodeRequestTypeEmailLink
  25. @brief Requests an email sign-in link.
  26. */
  27. case emailLink
  28. /** @var FIRGetOOBConfirmationCodeRequestTypeVerifyBeforeUpdateEmail
  29. @brief Requests an verify before update email.
  30. */
  31. case verifyBeforeUpdateEmail
  32. var value: String {
  33. switch self {
  34. case .passwordReset:
  35. return kPasswordResetRequestTypeValue
  36. case .verifyEmail:
  37. return kVerifyEmailRequestTypeValue
  38. case .emailLink:
  39. return kEmailLinkSignInTypeValue
  40. case .verifyBeforeUpdateEmail:
  41. return kVerifyBeforeUpdateEmailRequestTypeValue
  42. }
  43. }
  44. }
  45. private let kGetOobConfirmationCodeEndpoint = "getOobConfirmationCode"
  46. /** @var kRequestTypeKey
  47. @brief The name of the required "requestType" property in the request.
  48. */
  49. private let kRequestTypeKey = "requestType"
  50. /** @var kEmailKey
  51. @brief The name of the "email" property in the request.
  52. */
  53. private let kEmailKey = "email"
  54. /** @var kNewEmailKey
  55. @brief The name of the "newEmail" property in the request.
  56. */
  57. private let kNewEmailKey = "newEmail"
  58. /** @var kIDTokenKey
  59. @brief The key for the "idToken" value in the request. This is actually the STS Access Token,
  60. despite it's confusing (backwards compatiable) parameter name.
  61. */
  62. private let kIDTokenKey = "idToken"
  63. /** @var kContinueURLKey
  64. @brief The key for the "continue URL" value in the request.
  65. */
  66. private let kContinueURLKey = "continueUrl"
  67. /** @var kIosBundeIDKey
  68. @brief The key for the "iOS Bundle Identifier" value in the request.
  69. */
  70. private let kIosBundleIDKey = "iOSBundleId"
  71. /** @var kAndroidPackageNameKey
  72. @brief The key for the "Android Package Name" value in the request.
  73. */
  74. private let kAndroidPackageNameKey = "androidPackageName"
  75. /** @var kAndroidInstallAppKey
  76. @brief The key for the request parameter indicating whether the android app should be installed
  77. or not.
  78. */
  79. private let kAndroidInstallAppKey = "androidInstallApp"
  80. /** @var kAndroidMinimumVersionKey
  81. @brief The key for the "minimum Android version supported" value in the request.
  82. */
  83. private let kAndroidMinimumVersionKey = "androidMinimumVersion"
  84. /** @var kCanHandleCodeInAppKey
  85. @brief The key for the request parameter indicating whether the action code can be handled in
  86. the app or not.
  87. */
  88. private let kCanHandleCodeInAppKey = "canHandleCodeInApp"
  89. /** @var kDynamicLinkDomainKey
  90. @brief The key for the "dynamic link domain" value in the request.
  91. */
  92. private let kDynamicLinkDomainKey = "dynamicLinkDomain"
  93. /** @var kPasswordResetRequestTypeValue
  94. @brief The value for the "PASSWORD_RESET" request type.
  95. */
  96. private let kPasswordResetRequestTypeValue = "PASSWORD_RESET"
  97. /** @var kEmailLinkSignInTypeValue
  98. @brief The value for the "EMAIL_SIGNIN" request type.
  99. */
  100. private let kEmailLinkSignInTypeValue = "EMAIL_SIGNIN"
  101. /** @var kVerifyEmailRequestTypeValue
  102. @brief The value for the "VERIFY_EMAIL" request type.
  103. */
  104. private let kVerifyEmailRequestTypeValue = "VERIFY_EMAIL"
  105. /** @var kVerifyBeforeUpdateEmailRequestTypeValue
  106. @brief The value for the "VERIFY_AND_CHANGE_EMAIL" request type.
  107. */
  108. private let kVerifyBeforeUpdateEmailRequestTypeValue = "VERIFY_AND_CHANGE_EMAIL"
  109. /** @var kTenantIDKey
  110. @brief The key for the tenant id value in the request.
  111. */
  112. private let kTenantIDKey = "tenantId"
  113. /** @var kCaptchaResponseKey
  114. @brief The key for the "captchaResponse" value in the request.
  115. */
  116. private let kCaptchaResponseKey = "captchaResp"
  117. /** @var kClientType
  118. @brief The key for the "clientType" value in the request.
  119. */
  120. private let kClientType = "clientType"
  121. /** @var kRecaptchaVersion
  122. @brief The key for the "recaptchaVersion" value in the request.
  123. */
  124. private let kRecaptchaVersion = "recaptchaVersion"
  125. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  126. class GetOOBConfirmationCodeRequest: IdentityToolkitRequest, AuthRPCRequest {
  127. typealias Response = GetOOBConfirmationCodeResponse
  128. /** @property requestType
  129. @brief The types of OOB Confirmation Code to request.
  130. */
  131. let requestType: GetOOBConfirmationCodeRequestType
  132. /** @property email
  133. @brief The email of the user.
  134. @remarks For password reset.
  135. */
  136. private(set) var email: String?
  137. /** @property updatedEmail
  138. @brief The new email to be updated.
  139. @remarks For verifyBeforeUpdateEmail.
  140. */
  141. private(set) var updatedEmail: String?
  142. /** @property accessToken
  143. @brief The STS Access Token of the authenticated user.
  144. @remarks For email change.
  145. */
  146. private(set) var accessToken: String?
  147. /** @property continueURL
  148. @brief This URL represents the state/Continue URL in the form of a universal link.
  149. */
  150. private(set) var continueURL: String?
  151. /** @property iOSBundleID
  152. @brief The iOS bundle Identifier, if available.
  153. */
  154. private(set) var iOSBundleID: String?
  155. /** @property androidPackageName
  156. @brief The Android package name, if available.
  157. */
  158. private(set) var androidPackageName: String?
  159. /** @property androidMinimumVersion
  160. @brief The minimum Android version supported, if available.
  161. */
  162. private(set) var androidMinimumVersion: String?
  163. /** @property androidInstallIfNotAvailable
  164. @brief Indicates whether or not the Android app should be installed if not already available.
  165. */
  166. private(set) var androidInstallApp: Bool
  167. /** @property handleCodeInApp
  168. @brief Indicates whether the action code link will open the app directly or after being
  169. redirected from a Firebase owned web widget.
  170. */
  171. private(set) var handleCodeInApp: Bool
  172. /** @property dynamicLinkDomain
  173. @brief The Firebase Dynamic Link domain used for out of band code flow.
  174. */
  175. private(set) var dynamicLinkDomain: String?
  176. /** @property captchaResponse
  177. @brief Response to the captcha.
  178. */
  179. var captchaResponse: String?
  180. /** @property captchaResponse
  181. @brief The reCAPTCHA version.
  182. */
  183. var recaptchaVersion: String?
  184. /** @fn initWithRequestType:email:APIKey:
  185. @brief Designated initializer.
  186. @param requestType The types of OOB Confirmation Code to request.
  187. @param email The email of the user.
  188. @param newEmail The email of the user to be updated.
  189. @param accessToken The STS Access Token of the currently signed in user.
  190. @param actionCodeSettings An object of FIRActionCodeSettings which specifies action code
  191. settings to be applied to the OOB code request.
  192. @param requestConfiguration An object containing configurations to be added to the request.
  193. */
  194. required init(requestType: GetOOBConfirmationCodeRequestType,
  195. email: String?,
  196. newEmail: String?,
  197. accessToken: String?,
  198. actionCodeSettings: ActionCodeSettings?,
  199. requestConfiguration: AuthRequestConfiguration) {
  200. self.requestType = requestType
  201. self.email = email
  202. updatedEmail = newEmail
  203. self.accessToken = accessToken
  204. continueURL = actionCodeSettings?.url?.absoluteString
  205. iOSBundleID = actionCodeSettings?.iOSBundleID
  206. androidPackageName = actionCodeSettings?.androidPackageName
  207. androidMinimumVersion = actionCodeSettings?.androidMinimumVersion
  208. androidInstallApp = actionCodeSettings?.androidInstallIfNotAvailable ?? false
  209. handleCodeInApp = actionCodeSettings?.handleCodeInApp ?? false
  210. dynamicLinkDomain = actionCodeSettings?.dynamicLinkDomain
  211. super.init(
  212. endpoint: kGetOobConfirmationCodeEndpoint,
  213. requestConfiguration: requestConfiguration
  214. )
  215. }
  216. static func passwordResetRequest(email: String,
  217. actionCodeSettings: ActionCodeSettings?,
  218. requestConfiguration: AuthRequestConfiguration) ->
  219. GetOOBConfirmationCodeRequest {
  220. Self(requestType: .passwordReset,
  221. email: email,
  222. newEmail: nil,
  223. accessToken: nil,
  224. actionCodeSettings: actionCodeSettings,
  225. requestConfiguration: requestConfiguration)
  226. }
  227. static func verifyEmailRequest(accessToken: String,
  228. actionCodeSettings: ActionCodeSettings?,
  229. requestConfiguration: AuthRequestConfiguration) ->
  230. GetOOBConfirmationCodeRequest {
  231. Self(requestType: .verifyEmail,
  232. email: nil,
  233. newEmail: nil,
  234. accessToken: accessToken,
  235. actionCodeSettings: actionCodeSettings,
  236. requestConfiguration: requestConfiguration)
  237. }
  238. static func signInWithEmailLinkRequest(_ email: String,
  239. actionCodeSettings: ActionCodeSettings?,
  240. requestConfiguration: AuthRequestConfiguration)
  241. -> Self {
  242. Self(requestType: .emailLink,
  243. email: email,
  244. newEmail: nil,
  245. accessToken: nil,
  246. actionCodeSettings: actionCodeSettings,
  247. requestConfiguration: requestConfiguration)
  248. }
  249. static func verifyBeforeUpdateEmail(accessToken: String,
  250. newEmail: String,
  251. actionCodeSettings: ActionCodeSettings?,
  252. requestConfiguration: AuthRequestConfiguration)
  253. -> Self {
  254. Self(requestType: .verifyBeforeUpdateEmail,
  255. email: nil,
  256. newEmail: newEmail,
  257. accessToken: accessToken,
  258. actionCodeSettings: actionCodeSettings,
  259. requestConfiguration: requestConfiguration)
  260. }
  261. func unencodedHTTPRequestBody() throws -> [String: AnyHashable] {
  262. var body: [String: AnyHashable] = [
  263. kRequestTypeKey: requestType.value,
  264. ]
  265. // For password reset requests, we only need an email address in addition to the already
  266. // required fields.
  267. if case .passwordReset = requestType {
  268. body[kEmailKey] = email
  269. }
  270. // For verify email requests, we only need an STS Access Token in addition to the already
  271. // required fields.
  272. if case .verifyEmail = requestType {
  273. body[kIDTokenKey] = accessToken
  274. }
  275. // For email sign-in link requests, we only need an email address in addition to the already
  276. // required fields.
  277. if case .emailLink = requestType {
  278. body[kEmailKey] = email
  279. }
  280. // For email sign-in link requests, we only need an STS Access Token, a new email address in
  281. // addition to the already required fields.
  282. if case .verifyBeforeUpdateEmail = requestType {
  283. body[kNewEmailKey] = updatedEmail
  284. body[kIDTokenKey] = accessToken
  285. }
  286. if let continueURL = continueURL {
  287. body[kContinueURLKey] = continueURL
  288. }
  289. if let iOSBundleID = iOSBundleID {
  290. body[kIosBundleIDKey] = iOSBundleID
  291. }
  292. if let androidPackageName = androidPackageName {
  293. body[kAndroidPackageNameKey] = androidPackageName
  294. }
  295. if let androidMinimumVersion = androidMinimumVersion {
  296. body[kAndroidMinimumVersionKey] = androidMinimumVersion
  297. }
  298. if androidInstallApp {
  299. body[kAndroidInstallAppKey] = true
  300. }
  301. if handleCodeInApp {
  302. body[kCanHandleCodeInAppKey] = true
  303. }
  304. if let dynamicLinkDomain {
  305. body[kDynamicLinkDomainKey] = dynamicLinkDomain
  306. }
  307. if let captchaResponse {
  308. body[kCaptchaResponseKey] = captchaResponse
  309. }
  310. body[kClientType] = clientType
  311. if let recaptchaVersion {
  312. body[kRecaptchaVersion] = recaptchaVersion
  313. }
  314. if let tenantID {
  315. body[kTenantIDKey] = tenantID
  316. }
  317. return body
  318. }
  319. func injectRecaptchaFields(recaptchaResponse: String?, recaptchaVersion: String) {
  320. captchaResponse = recaptchaResponse
  321. self.recaptchaVersion = recaptchaVersion
  322. }
  323. }