From 1fc52ab0e172e7c5a961f975a76c2611f4f22852 Mon Sep 17 00:00:00 2001 From: Nick Cooke <36927374+ncooke3@users.noreply.github.com> Date: Sat, 7 Sep 2024 21:56:30 -0400 Subject: [PATCH] [Release] Cherry-pick #13592 into `release-11.2` (#13602) --- FirebaseAuth/CHANGELOG.md | 3 ++ .../RPC/Proto/AuthProtoMFAEnrollment.swift | 2 ++ .../TOTP/TOTPMultiFactorInfo.swift | 11 +++---- FirebaseAuth/Tests/Unit/UserTests.swift | 33 +++++++++++++------ 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/FirebaseAuth/CHANGELOG.md b/FirebaseAuth/CHANGELOG.md index 96771bafd05..81388ecef86 100644 --- a/FirebaseAuth/CHANGELOG.md +++ b/FirebaseAuth/CHANGELOG.md @@ -7,6 +7,9 @@ in methods and a `nil` error. In such cases, an empty array is instead returned with the `nil` error. (#13550) - [Fixed] Fixed user session persistence in multi tenant projects. Introduced in 11.0.0. (#13565) +- [Fixed] Fixed encoding crash that occurs when using TOTP multi-factor + authentication. Note that this fix will not be in the 11.2.0 zip and Carthage + distributions, but will be included from 11.3.0 onwards. (#13591) # 11.1.0 - [fixed] Fixed `Swift.error` conformance for `AuthErrorCode`. (#13430) diff --git a/FirebaseAuth/Sources/Swift/Backend/RPC/Proto/AuthProtoMFAEnrollment.swift b/FirebaseAuth/Sources/Swift/Backend/RPC/Proto/AuthProtoMFAEnrollment.swift index e742cf8cf77..494252ac56e 100644 --- a/FirebaseAuth/Sources/Swift/Backend/RPC/Proto/AuthProtoMFAEnrollment.swift +++ b/FirebaseAuth/Sources/Swift/Backend/RPC/Proto/AuthProtoMFAEnrollment.swift @@ -16,6 +16,8 @@ import Foundation class AuthProtoMFAEnrollment: NSObject, AuthProto { let phoneInfo: String? + // In practice, this will be an empty dictionary. The presence of which + // indicates TOTP MFA enrollment rather than phone MFA enrollment. let totpInfo: NSObject? let mfaEnrollmentID: String? let displayName: String? diff --git a/FirebaseAuth/Sources/Swift/MultiFactor/TOTP/TOTPMultiFactorInfo.swift b/FirebaseAuth/Sources/Swift/MultiFactor/TOTP/TOTPMultiFactorInfo.swift index 2273b2aea9e..b1c3f7b7d95 100644 --- a/FirebaseAuth/Sources/Swift/MultiFactor/TOTP/TOTPMultiFactorInfo.swift +++ b/FirebaseAuth/Sources/Swift/MultiFactor/TOTP/TOTPMultiFactorInfo.swift @@ -22,19 +22,18 @@ import Foundation /// /// This class is available on iOS only. class TOTPMultiFactorInfo: MultiFactorInfo { - /// This is the totp info for the second factor. - let totpInfo: NSObject? - /// Initialize the AuthProtoMFAEnrollment instance with proto. /// - Parameter proto: AuthProtoMFAEnrollment proto object. init(proto: AuthProtoMFAEnrollment) { - totpInfo = proto.totpInfo super.init(proto: proto, factorID: PhoneMultiFactorInfo.TOTPMultiFactorID) } - @available(*, unavailable) required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") + super.init(coder: coder) + } + + override class var supportsSecureCoding: Bool { + super.supportsSecureCoding } } #endif diff --git a/FirebaseAuth/Tests/Unit/UserTests.swift b/FirebaseAuth/Tests/Unit/UserTests.swift index 619efac790a..77c1449792f 100644 --- a/FirebaseAuth/Tests/Unit/UserTests.swift +++ b/FirebaseAuth/Tests/Unit/UserTests.swift @@ -141,12 +141,21 @@ class UserTests: RPCBaseTests { "phoneNumber": kPhoneNumber, "createdAt": String(Int(kCreationDateTimeIntervalInSeconds) * 1000), // to nanoseconds "lastLoginAt": String(Int(kLastSignInDateTimeIntervalInSeconds) * 1000), - "mfaInfo": [[ - "phoneInfo": kPhoneInfo, - "mfaEnrollmentId": kEnrollmentID, - "displayName": kDisplayName, - "enrolledAt": kEnrolledAt, - ]], + "mfaInfo": [ + [ + "phoneInfo": kPhoneInfo, + "mfaEnrollmentId": kEnrollmentID, + "displayName": kDisplayName, + "enrolledAt": kEnrolledAt, + ], + [ + // In practice, this will be an empty dictionary. + "totpInfo": [AnyHashable: AnyHashable](), + "mfaEnrollmentId": kEnrollmentID, + "displayName": kDisplayName, + "enrolledAt": kEnrolledAt, + ] as [AnyHashable: AnyHashable], + ], ]] let expectation = self.expectation(description: #function) @@ -346,11 +355,15 @@ class UserTests: RPCBaseTests { // Verify MultiFactorInfo properties. let enrolledFactors = try XCTUnwrap(user.multiFactor.enrolledFactors) + XCTAssertEqual(enrolledFactors.count, 2) XCTAssertEqual(enrolledFactors[0].factorID, PhoneMultiFactorInfo.PhoneMultiFactorID) - XCTAssertEqual(enrolledFactors[0].uid, kEnrollmentID) - XCTAssertEqual(enrolledFactors[0].displayName, self.kDisplayName) - let date = try XCTUnwrap(enrolledFactors[0].enrollmentDate) - XCTAssertEqual("\(date)", kEnrolledAtMatch) + XCTAssertEqual(enrolledFactors[1].factorID, PhoneMultiFactorInfo.TOTPMultiFactorID) + for enrolledFactor in enrolledFactors { + XCTAssertEqual(enrolledFactor.uid, kEnrollmentID) + XCTAssertEqual(enrolledFactor.displayName, self.kDisplayName) + let date = try XCTUnwrap(enrolledFactor.enrollmentDate) + XCTAssertEqual("\(date)", kEnrolledAtMatch) + } #endif } catch { XCTFail("Caught an error in \(#function): \(error)")