Skip to content

Commit bcc293c

Browse files
authored
[Auth] Fix async/await crash from implicitly unwrapped nil error (#13472)
1 parent 7469d83 commit bcc293c

File tree

5 files changed

+62
-36
lines changed

5 files changed

+62
-36
lines changed

FirebaseAuth/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
- [added] Added custom provider support to `AuthProviderID`. Note that this change will be breaking
44
to any code that implemented an exhaustive `switch` on `AuthProviderID` in 11.0.0 - the `switch`
55
will need expansion. (#13429)
6+
- [fixed] Fix crash introduced in 11.0.0 in phone authentication flow from
7+
implicitly unwrapping `nil` error after a token timeout. (#13470)
68

79
# 11.0.0
810
- [fixed] Fixed auth domain matching code to prioritize matching `firebaseapp.com` over `web.app`

FirebaseAuth/Sources/Swift/SystemService/AuthAPNSTokenManager.swift

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@
4949
/// token becomes available, or when timeout occurs, whichever happens earlier.
5050
///
5151
/// This function is internal to make visible for tests.
52-
func getTokenInternal(callback: @escaping (AuthAPNSToken?, Error?) -> Void) {
52+
func getTokenInternal(callback: @escaping (Result<AuthAPNSToken, Error>) -> Void) {
5353
if let token = tokenStore {
54-
callback(token, nil)
54+
callback(.success(token))
5555
return
5656
}
5757
if pendingCallbacks.count > 0 {
@@ -68,18 +68,19 @@
6868
kAuthGlobalWorkQueue.asyncAfter(deadline: deadline) {
6969
// Only cancel if the pending callbacks remain the same, i.e., not triggered yet.
7070
if applicableCallbacks.count == self.pendingCallbacks.count {
71-
self.callback(withToken: nil, error: nil)
71+
self.callback(.failure(AuthErrorUtils.missingAppTokenError(underlyingError: nil)))
7272
}
7373
}
7474
}
7575

7676
func getToken() async throws -> AuthAPNSToken {
7777
return try await withCheckedThrowingContinuation { continuation in
78-
self.getTokenInternal { token, error in
79-
if let token {
78+
self.getTokenInternal { result in
79+
switch result {
80+
case let .success(token):
8081
continuation.resume(returning: token)
81-
} else {
82-
continuation.resume(throwing: error!)
82+
case let .failure(error):
83+
continuation.resume(throwing: error)
8384
}
8485
}
8586
}
@@ -105,7 +106,7 @@
105106
newToken = AuthAPNSToken(withData: setToken.data, type: detectedTokenType)
106107
}
107108
tokenStore = newToken
108-
callback(withToken: newToken, error: nil)
109+
callback(.success(newToken))
109110
}
110111
}
111112

@@ -115,18 +116,18 @@
115116
/// Cancels any pending `getTokenWithCallback:` request.
116117
/// - Parameter error: The error to return .
117118
func cancel(withError error: Error) {
118-
callback(withToken: nil, error: error)
119+
callback(.failure(error))
119120
}
120121

121122
/// Enable unit test faking.
122123
var application: AuthAPNSTokenApplication
123-
private var pendingCallbacks: [(AuthAPNSToken?, Error?) -> Void] = []
124+
private var pendingCallbacks: [(Result<AuthAPNSToken, Error>) -> Void] = []
124125

125-
private func callback(withToken token: AuthAPNSToken?, error: Error?) {
126+
private func callback(_ result: Result<AuthAPNSToken, Error>) {
126127
let pendingCallbacks = self.pendingCallbacks
127128
self.pendingCallbacks = []
128129
for callback in pendingCallbacks {
129-
callback(token, error)
130+
callback(result)
130131
}
131132
}
132133

FirebaseAuth/Tests/SampleSwift/AuthenticationExample/ViewControllers/AuthViewController.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -468,14 +468,14 @@ class AuthViewController: UIViewController, DataSourceProviderDelegate {
468468
}
469469

470470
private func verifyClient() {
471-
AppManager.shared.auth().tokenManager.getTokenInternal { token, error in
472-
if token == nil {
471+
AppManager.shared.auth().tokenManager.getTokenInternal { result in
472+
guard case let .success(token) = result else {
473473
print("Verify iOS Client failed.")
474474
return
475475
}
476476
let request = VerifyClientRequest(
477-
withAppToken: token?.string,
478-
isSandbox: token?.type == .sandbox,
477+
withAppToken: token.string,
478+
isSandbox: token.type == .sandbox,
479479
requestConfiguration: AppManager.shared.auth().requestConfiguration
480480
)
481481

FirebaseAuth/Tests/Unit/AuthAPNSTokenManagerTests.swift

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,29 @@
6363
XCTAssertFalse(fakeApplication!.registerCalled)
6464
var firstCallbackCalled = false
6565
let manager = try XCTUnwrap(manager)
66-
manager.getTokenInternal { token, error in
66+
manager.getTokenInternal { result in
6767
firstCallbackCalled = true
68-
XCTAssertEqual(token?.data, self.data)
69-
XCTAssertEqual(token?.type, .sandbox)
70-
XCTAssertNil(error)
68+
switch result {
69+
case let .success(token):
70+
XCTAssertEqual(token.data, self.data)
71+
XCTAssertEqual(token.type, .sandbox)
72+
case let .failure(error):
73+
XCTFail("Unexpected error: \(error)")
74+
}
7175
}
7276
XCTAssertFalse(firstCallbackCalled)
7377

7478
// Add second callback, which is yet to be called either.
7579
var secondCallbackCalled = false
76-
manager.getTokenInternal { token, error in
80+
manager.getTokenInternal { result in
7781
secondCallbackCalled = true
78-
XCTAssertEqual(token?.data, self.data)
79-
XCTAssertEqual(token?.type, .sandbox)
80-
XCTAssertNil(error)
82+
switch result {
83+
case let .success(token):
84+
XCTAssertEqual(token.data, self.data)
85+
XCTAssertEqual(token.type, .sandbox)
86+
case let .failure(error):
87+
XCTFail("Unexpected error: \(error)")
88+
}
8189
}
8290
XCTAssertFalse(secondCallbackCalled)
8391

@@ -96,11 +104,15 @@
96104

97105
// Add third callback, which should be called back immediately.
98106
var thirdCallbackCalled = false
99-
manager.getTokenInternal { token, error in
107+
manager.getTokenInternal { result in
100108
thirdCallbackCalled = true
101-
XCTAssertEqual(token?.data, self.data)
102-
XCTAssertEqual(token?.type, .sandbox)
103-
XCTAssertNil(error)
109+
switch result {
110+
case let .success(token):
111+
XCTAssertEqual(token.data, self.data)
112+
XCTAssertEqual(token.type, .sandbox)
113+
case let .failure(error):
114+
XCTFail("Unexpected error: \(error)")
115+
}
104116
}
105117
XCTAssertTrue(thirdCallbackCalled)
106118

@@ -123,9 +135,16 @@
123135

124136
// Add callback to time out.
125137
let expectation = self.expectation(description: #function)
126-
manager.getTokenInternal { token, error in
127-
XCTAssertNil(token)
128-
XCTAssertNil(error)
138+
manager.getTokenInternal { result in
139+
switch result {
140+
case let .success(token):
141+
XCTFail("Unexpected success: \(token)")
142+
case let .failure(error):
143+
XCTAssertEqual(
144+
error as NSError,
145+
AuthErrorUtils.missingAppTokenError(underlyingError: nil) as NSError
146+
)
147+
}
129148
expectation.fulfill()
130149
}
131150
// Time out.
@@ -153,9 +172,13 @@
153172

154173
// Add callback to cancel.
155174
var callbackCalled = false
156-
manager.getTokenInternal { token, error in
157-
XCTAssertNil(token)
158-
XCTAssertEqual(error as? NSError, self.error as NSError)
175+
manager.getTokenInternal { result in
176+
switch result {
177+
case let .success(token):
178+
XCTFail("Unexpected success: \(token)")
179+
case let .failure(error):
180+
XCTAssertEqual(error as NSError, self.error as NSError)
181+
}
159182
XCTAssertFalse(callbackCalled) // verify callback is not called twice
160183
callbackCalled = true
161184
}

FirebaseAuth/Tests/Unit/PhoneAuthProviderTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -690,9 +690,9 @@
690690
}
691691

692692
class FakeTokenManager: AuthAPNSTokenManager {
693-
override func getTokenInternal(callback: @escaping (AuthAPNSToken?, Error?) -> Void) {
693+
override func getTokenInternal(callback: @escaping (Result<AuthAPNSToken, Error>) -> Void) {
694694
let error = NSError(domain: "dummy domain", code: AuthErrorCode.missingAppToken.rawValue)
695-
callback(nil, error)
695+
callback(.failure(error))
696696
}
697697
}
698698

0 commit comments

Comments
 (0)