firebase / firebase-ios-sdk

Firebase SDK for Apple App Development
https://firebase.google.com
Apache License 2.0
5.55k stars 1.45k forks source link

Auth.auth().canHandleNotification always returns false #13502

Open lukavujnovac opened 4 weeks ago

lukavujnovac commented 4 weeks ago

Description

Hi,

the issue is Auth.auth().canHandleNotification(...) always returning false and I cannot verify phone auth. I followed the docs and my App Delegate looks as follows:

class AppDelegate: NSObject, UIApplicationDelegate, MessagingDelegate, UNUserNotificationCenterDelegate  {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        FirebaseApp.configure()
        UNUserNotificationCenter.current().delegate = self
        NotificationService().requestAuthorization()
        application.registerForRemoteNotifications()
        Messaging.messaging().delegate = self

        return true
    }

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        Messaging.messaging().apnsToken = deviceToken
        Auth.auth().setAPNSToken(deviceToken, type: .prod)
    }

    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        let firebaseAuth = Auth.auth()
        Messaging.messaging().appDidReceiveMessage(userInfo)
        print(userInfo)
        if Auth.auth().canHandleNotification(userInfo) {
            completionHandler(.noData)
        }
        completionHandler(.noData)
    }

    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        Messaging.messaging().token { token, error in
            if let error {
                log.error("failed to fetch FCM registration token \(error)")
            } else if let token {
                print("fcm \(token)")
                print(token)
            }
        }
    }

    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        if Auth.auth().canHandle(url) {
            return true
        }
        return false
    }
}

push notifications are enables background refresh and remote notifications enabled sizzling is disabled.

Reproducing the issue

No specific steps needed

Firebase SDK Version

10.29.0

Xcode Version

15.3

Installation Method

Swift Package Manager

Firebase Product(s)

Authentication

Targeted Platforms

iOS

Relevant Log Output

the notification prints out:

[AnyHashable("com.google.firebase.auth"): {
    warning = "This fake notification should be forwarded to Firebase Auth.";
}]
Error Domain=FIRAuthErrorDomain Code=17054 "If app delegate swizzling is disabled, remote notifications received by UIApplicationDelegate need to be forwarded to FIRAuth's canHandleNotificaton: method." UserInfo={NSLocalizedDescription=If app delegate swizzling is disabled, remote notifications received by UIApplicationDelegate need to be forwarded to FIRAuth's canHandleNotificaton: method., FIRAuthErrorUserInfoNameKey=ERROR_NOTIFICATION_NOT_FORWARDED}

If using Swift Package Manager, the project's Package.resolved

Expand Package.resolved snippet
```json Replace this line with the contents of your Package.resolved. ```

If using CocoaPods, the project's Podfile.lock

Expand Podfile.lock snippet
```yml Replace this line with the contents of your Podfile.lock! ```
google-oss-bot commented 4 weeks ago

I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

lukavujnovac commented 3 weeks ago

Update: this code sometimes works for test phone numbers, but sometimes does not. Any help?

ncooke3 commented 3 weeks ago

Hi @lukavujnovac, do you have the Background Modes capability enabled? If so, ensure that the Background fetch and Remote notifications modes are selected.

lukavujnovac commented 3 weeks ago

Hi @lukavujnovac, do you have the Background Modes capability enabled? If so, ensure that the Background fetch and Remote notifications modes are selected.

Hey @ncooke3, yes I do, I have background fetch, remote notifications and background processing all selected under background modes

rizafran commented 2 weeks ago

Hi @lukavujnovac, I tried to reproduce the issue and I got the same behavior. I've raised this to our engineering team to investigate what's causing the issue. Thanks.

lukavujnovac commented 2 weeks ago

Hi @lukavujnovac, I tried to reproduce the issue and I got the same behavior. I've raised this to our engineering team to investigate what's causing the issue. Thanks.

hey @rizafran this code works a few times if I set AuthAPNSTokenType to unknown. After a while I get the same error as above, is it possible it's to prevent spam? I have a paid firebase account which should allow up to 1000 SMSs a day.

ncooke3 commented 2 weeks ago

Hi @lukavujnovac, the APNS token type should be .unknown. This will allow the SDK to infer whether the APNS token is a production or a sandbox token based on the environment. I'm going to update our docs accordingly.

When making this change, are you seeing any cases where Auth.auth().canHandleNotification is not working correctly or is this part fixed by switching to .unknown?

Unrelated, but I noticed in your shared snippet that there was a path that calls the completion handler twice. I'm not sure if this could have any side effects in Apple's code, but it may be better to add a return after the first one.

if Auth.auth().canHandleNotification(userInfo) { completionHandler(.noData) } completionHandler(.noData)

lukavujnovac commented 2 weeks ago

Hi @lukavujnovac, the APNS token type should be .unknown. This will allow the SDK to infer whether the APNS token is a production or a sandbox token based on the environment. I'm going to update our docs accordingly.

When making this change, are you seeing any cases where Auth.auth().canHandleNotification is not working correctly or is this part fixed by switching to .unknown?

Unrelated, but I noticed in your shared snippet that there was a path that calls the completion handler twice. I'm not sure if this could have any side effects in Apple's code, but it may be better to add a return after the first one.

if Auth.auth().canHandleNotification(userInfo) {

        completionHandler(.noData)

    }

    completionHandler(.noData)

hi @ncooke3, that part is partly fixed by using the .unknown. As stated above, code works for a few times before receiving the same error. Could this be some spam protection? I do have a paid firebase account which should allow me up to 1000 SMSs a day. Thanks for the tip!

ncooke3 commented 2 weeks ago

Could this be some spam protection?

I don't think so. When the quota is reached, I wouldn't expect to see the Error Domain=FIRAuthErrorDomain Code=17054 "If app delegate swizzling is disabled... error, instead you'll see another error like error code 17052.

When you encounter the "If app delegate swizzling is disabled... error, does the reCAPTCHA flow get presented?

When the test notification is fired, it must be intercepted in a certain amount of time. If you are using the debugger and pausing at breakpoints, it's possible that it may the timeout may be reached. Do you happen to be using the debugger when the error occurs?

lukavujnovac commented 2 weeks ago

Could this be some spam protection?

I don't think so. When the quota is reached, I wouldn't expect to see the Error Domain=FIRAuthErrorDomain Code=17054 "If app delegate swizzling is disabled... error, instead you'll see another error like error code 17052.

When you encounter the "If app delegate swizzling is disabled... error, does the reCAPTCHA flow get presented?

When the test notification is fired, it must be intercepted in a certain amount of time. If you are using the debugger and pausing at breakpoints, it's possible that it may the timeout may be reached. Do you happen to be using the debugger when the error occurs?

@ncooke3 reCAPTCHA flow only gets triggered if I test on a simulator, and I am unable to proceed with phone auth on the simulator after that, I don't know if that's a separate issue or intended behavior. Every day this code works about 3-5 times, after that I get the error listed above. There are no other changes in the environment, everything is the same for working and non-working cases, but I could not find any differences. If needed, I can give you more info about the project and the firebase console setup.

ncooke3 commented 2 weeks ago

Thanks, @lukavujnovac. I was able to reproduce this behavior but I was not able to pinpoint what's going wrong. The first few logins were successful, then I got the "If app delegate swizzling is disabled... error, and when I stopped getting that, I got an error that my device had been blocked temporarily.

lukavujnovac commented 2 weeks ago

Thanks, @lukavujnovac. I was able to reproduce this behavior but I was not able to pinpoint what's going wrong. The first few logins were successful, then I got the "If app delegate swizzling is disabled... error, and when I stopped getting that, I got an error that my device had been blocked temporarily.

Are there any workarounds for this currently? This is currently a release blocker for me. If you need any additional info from me to help debug lmk.

ncooke3 commented 2 weeks ago

The only possible workaround may be to enable swizzling, but I understand that may be infeasible depending on your requirements. While setting the APNS type to .unknown is only a partial fix, this remaining problem is hopefully rare in practice since it seems to be only appear after multiple login attempts.