MultiFactorResolver.swift 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  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. #if os(iOS)
  16. /// The subclass of base class `MultiFactorAssertion`, used to assert ownership of a phone
  17. /// second factor.
  18. ///
  19. /// This class is available on iOS only.
  20. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  21. @objc(FIRMultiFactorResolver)
  22. open class MultiFactorResolver: NSObject {
  23. /// The opaque session identifier for the current sign-in flow.
  24. @objc public let session: MultiFactorSession
  25. /// The list of hints for the second factors needed to complete the sign-in for the current
  26. /// session.
  27. @objc public let hints: [MultiFactorInfo]
  28. /// The Auth reference for the current `MultiResolver`.
  29. @objc public let auth: Auth
  30. /// A helper function to help users complete sign in with a second factor using a
  31. /// `MultiFactorAssertion` confirming the user successfully completed the second factor
  32. /// challenge.
  33. /// - Parameter completion: The block invoked when the request is complete, or fails.
  34. @objc(resolveSignInWithAssertion:completion:)
  35. open func resolveSignIn(with assertion: MultiFactorAssertion,
  36. completion: ((AuthDataResult?, Error?) -> Void)? = nil) {
  37. var finalizedMFARequestInfo: AuthProto?
  38. if let totpAssertion = assertion as? TOTPMultiFactorAssertion {
  39. switch totpAssertion.secretOrID {
  40. case .secret: fatalError("Missing enrollmentID in totpAssertion")
  41. case let .enrollmentID(enrollmentID):
  42. finalizedMFARequestInfo = AuthProtoFinalizeMFATOTPSignInRequestInfo(
  43. mfaEnrollmentID: enrollmentID,
  44. verificationCode: totpAssertion.oneTimePassword
  45. )
  46. }
  47. } else {
  48. let phoneAssertion = assertion as? PhoneMultiFactorAssertion
  49. guard let credential = phoneAssertion?.authCredential else {
  50. fatalError("Internal Error: Missing credential")
  51. }
  52. switch credential.credentialKind {
  53. case .phoneNumber: fatalError("Internal Error: Missing verificationCode")
  54. case let .verification(verificationID, code):
  55. finalizedMFARequestInfo =
  56. AuthProtoFinalizeMFAPhoneRequestInfo(
  57. sessionInfo: verificationID,
  58. verificationCode: code
  59. )
  60. }
  61. }
  62. let request = FinalizeMFASignInRequest(
  63. mfaPendingCredential: mfaPendingCredential,
  64. verificationInfo: finalizedMFARequestInfo,
  65. requestConfiguration: auth.requestConfiguration
  66. )
  67. Task {
  68. do {
  69. let response = try await AuthBackend.call(with: request)
  70. let user = try await self.auth.completeSignIn(withAccessToken: response.idToken,
  71. accessTokenExpirationDate: nil,
  72. refreshToken: response.refreshToken,
  73. anonymous: false)
  74. let result = AuthDataResult(withUser: user, additionalUserInfo: nil)
  75. let decoratedCallback = self.auth
  76. .signInFlowAuthDataResultCallback(byDecorating: completion)
  77. decoratedCallback(result, nil)
  78. } catch {
  79. if let completion {
  80. completion(nil, error)
  81. }
  82. }
  83. }
  84. }
  85. /// A helper function to help users complete sign in with a second factor using a
  86. /// `MultiFactorAssertion` confirming the user successfully completed the second factor
  87. /// challenge.
  88. @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
  89. open func resolveSignIn(with assertion: MultiFactorAssertion) async throws -> AuthDataResult {
  90. return try await withCheckedThrowingContinuation { continuation in
  91. self.resolveSignIn(with: assertion) { result, error in
  92. if let result {
  93. continuation.resume(returning: result)
  94. } else {
  95. continuation.resume(throwing: error!)
  96. }
  97. }
  98. }
  99. }
  100. let mfaPendingCredential: String?
  101. init(with mfaPendingCredential: String?, hints: [MultiFactorInfo], auth: Auth) {
  102. self.mfaPendingCredential = mfaPendingCredential
  103. self.hints = hints
  104. self.auth = auth
  105. session = MultiFactorSession(mfaCredential: mfaPendingCredential)
  106. }
  107. }
  108. #endif