| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- // Copyright 2023 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- import Foundation
- #if os(iOS)
- /** @class FIRMultiFactor
- @brief The interface defining the multi factor related properties and operations pertaining to a
- user.
- This class is available on iOS only.
- */
- @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
- @objc(FIRMultiFactor) public class MultiFactor: NSObject, NSSecureCoding {
- @objc public var enrolledFactors: [MultiFactorInfo]?
- /** @fn getSessionWithCompletion:
- @brief Get a session for a second factor enrollment operation.
- @param completion A block with the session identifier for a second factor enrollment operation.
- This is used to identify the current user trying to enroll a second factor.
- */
- @objc(getSessionWithCompletion:)
- public func getSessionWithCompletion(_ completion: ((MultiFactorSession?, Error?) -> Void)?) {
- let session = MultiFactorSession.sessionForCurrentUser
- if let completion {
- completion(session, nil)
- }
- }
- /** @fn getSessionWithCompletion:
- @brief Get a session for a second factor enrollment operation.
- @param completion A block with the session identifier for a second factor enrollment operation.
- This is used to identify the current user trying to enroll a second factor.
- */
- @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
- public func session() async throws -> MultiFactorSession {
- return try await withCheckedThrowingContinuation { continuation in
- self.getSessionWithCompletion { session, error in
- if let session {
- continuation.resume(returning: session)
- } else {
- continuation.resume(throwing: error!)
- }
- }
- }
- }
- /** @fn enrollWithAssertion:displayName:completion:
- @brief Enrolls a second factor as identified by the `MultiFactorAssertion` parameter for the
- current user.
- @param displayName An optional display name associated with the multi factor to enroll.
- @param completion The block invoked when the request is complete, or fails.
- */
- @objc(enrollWithAssertion:displayName:completion:)
- public func enroll(with assertion: MultiFactorAssertion,
- displayName: String?,
- completion: ((Error?) -> Void)?) {
- let phoneAssertion = assertion as? PhoneMultiFactorAssertion
- guard let credential = phoneAssertion?.authCredential else {
- fatalError("Internal Error: Missing credential")
- }
- switch credential.credentialKind {
- case .phoneNumber: fatalError("Internal Error: Missing verificationCode")
- case let .verification(verificationID, code):
- let finalizeMFAPhoneRequestInfo =
- AuthProtoFinalizeMFAPhoneRequestInfo(sessionInfo: verificationID, verificationCode: code)
- guard let user = user else {
- fatalError("Internal Auth error: failed to get user enrolling in MultiFactor")
- }
- let request = FinalizeMFAEnrollmentRequest(
- idToken: self.user?.rawAccessToken(),
- displayName: displayName,
- verificationInfo: finalizeMFAPhoneRequestInfo,
- requestConfiguration: user.requestConfiguration
- )
- AuthBackend.post(with: request) { rawResponse, error in
- if let error {
- if let completion {
- completion(error)
- }
- } else if let response = rawResponse {
- user.auth?.completeSignIn(withAccessToken: response.idToken,
- accessTokenExpirationDate: nil,
- refreshToken: response.refreshToken,
- anonymous: false) { _, error in
- if error != nil {
- try? user.auth?.signOut()
- }
- if let completion {
- completion(error)
- }
- }
- }
- }
- }
- }
- /** @fn enrollWithAssertion:displayName:completion:
- @brief Enrolls a second factor as identified by the `MultiFactorAssertion` parameter for the
- current user.
- @param displayName An optional display name associated with the multi factor to enroll.
- @param completion The block invoked when the request is complete, or fails.
- */
- @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
- public func enroll(with assertion: MultiFactorAssertion, displayName: String?) async throws {
- return try await withCheckedThrowingContinuation { continuation in
- self.enroll(with: assertion, displayName: displayName) { error in
- if let error {
- continuation.resume(throwing: error)
- } else {
- continuation.resume()
- }
- }
- }
- }
- /** @fn unenrollWithInfo:completion:
- @brief Unenroll the given multi factor.
- @param completion The block invoked when the request to send the verification email is complete,
- or fails.
- */
- @objc(unenrollWithInfo:completion:)
- public func unenroll(with factorInfo: MultiFactorInfo,
- completion: ((Error?) -> Void)?) {
- unenroll(withFactorUID: factorInfo.uid, completion: completion)
- }
- /** @fn unenrollWithInfo:completion:
- @brief Unenroll the given multi factor.
- @param completion The block invoked when the request to send the verification email is complete,
- or fails.
- */
- @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
- public func unenroll(with factorInfo: MultiFactorInfo) async throws {
- try await unenroll(withFactorUID: factorInfo.uid)
- }
- /** @fn unenrollWithFactorUID:completion:
- @brief Unenroll the given multi factor.
- @param completion The block invoked when the request to send the verification email is complete,
- or fails.
- */
- @objc(unenrollWithFactorUID:completion:)
- public func unenroll(withFactorUID factorUID: String,
- completion: ((Error?) -> Void)?) {
- guard let user = user else {
- fatalError("Internal Auth error: failed to get user unenrolling in MultiFactor")
- }
- let request = WithdrawMFARequest(idToken: user.rawAccessToken(),
- mfaEnrollmentID: factorUID,
- requestConfiguration: user.requestConfiguration)
- AuthBackend.post(with: request) { rawResponse, error in
- if let error {
- if let completion {
- completion(error)
- }
- } else {
- guard let response = rawResponse else {
- fatalError("TODO")
- }
- user.auth?.completeSignIn(withAccessToken: response.idToken,
- accessTokenExpirationDate: nil,
- refreshToken: response.refreshToken,
- anonymous: false) { signInUser, error in
- if let completion {
- completion(error)
- }
- }
- }
- }
- }
- @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
- public func unenroll(withFactorUID factorUID: String) async throws {
- return try await withCheckedThrowingContinuation { continuation in
- self.unenroll(withFactorUID: factorUID) { error in
- if let error {
- continuation.resume(throwing: error)
- } else {
- continuation.resume()
- }
- }
- }
- }
- weak var user: User?
- convenience init(withMFAEnrollments mfaEnrollments: [AuthProtoMFAEnrollment]) {
- self.init()
- var multiFactorInfoArray: [MultiFactorInfo] = []
- for enrollment in mfaEnrollments {
- let multiFactorInfo = PhoneMultiFactorInfo(proto: enrollment)
- multiFactorInfoArray.append(multiFactorInfo)
- }
- enrolledFactors = multiFactorInfoArray
- }
- override init() {}
- // MARK: - NSSecureCoding
- private let kEnrolledFactorsCodingKey = "enrolledFactors"
- public static var supportsSecureCoding: Bool {
- true
- }
- public func encode(with coder: NSCoder) {
- coder.encode(enrolledFactors, forKey: kEnrolledFactorsCodingKey)
- // Do not encode `user` weak property.
- }
- public required init?(coder: NSCoder) {
- let enrolledFactors = coder
- .decodeObject(forKey: kEnrolledFactorsCodingKey) as? [MultiFactorInfo]
- self.enrolledFactors = enrolledFactors
- // Do not decode `user` weak property.
- }
- }
- #endif
|