Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Auth] Forward secure coding calls for TOTPMultiFactorInfo #13592

Merged
merged 7 commits into from
Sep 7, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions FirebaseAuth/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
ncooke3 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Despite being marked unavailable, this class's init?(coder:) is actually called when totp is registered:

coder.encode(enrolledFactors, forKey: kEnrolledFactorsCodingKey)

Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,22 @@ import Foundation
///
/// This class is available on iOS only.
class TOTPMultiFactorInfo: MultiFactorInfo {
/// This is the totp info for the second factor.
let totpInfo: NSObject?
Comment on lines -25 to -26
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typing this with a more specific type (dictionary) added a more code to cast it during encoding and decoding. I stepped back and see that this property is never read from (it's only read from from the AuthProtoMFAEnrollment.swift class). I think it's better to just remove it as it's addition is more confusing.


/// 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 func encode(with coder: NSCoder) {
super.encode(with: coder)
}

override class var supportsSecureCoding: Bool {
super.supportsSecureCoding
}
}
#endif
2 changes: 1 addition & 1 deletion FirebaseAuth/Tests/Unit/Fakes/FakeBackendRPCIssuer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class FakeBackendRPCIssuer: NSObject, AuthBackendRPCIssuer {
var respondBlock: (() throws -> Void)?
var nextRespondBlock: (() throws -> Void)?

var fakeGetAccountProviderJSON: [[String: AnyHashable]]?
var fakeGetAccountProviderJSON: [[String: Any]]?
ncooke3 marked this conversation as resolved.
Show resolved Hide resolved
var fakeSecureTokenServiceJSON: [String: AnyHashable]?
var secureTokenNetworkError: NSError?
var secureTokenErrorString: String?
Expand Down
33 changes: 23 additions & 10 deletions FirebaseAuth/Tests/Unit/UserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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: Any](),
"mfaEnrollmentId": kEnrollmentID,
"displayName": kDisplayName,
"enrolledAt": kEnrolledAt,
],
],
]]

let expectation = self.expectation(description: #function)
Expand Down Expand Up @@ -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)")
Expand Down
Loading