firebase / firebase-ios-sdk

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

Firebase App Check Crash #12365

Closed johnfrancmartin closed 4 months ago

johnfrancmartin commented 9 months ago

Description

App crashing shortly after launch, due to an issue fetching Firebase App Check token.

We're using AppAttest, with code very similar to provided default code i.e.

class YourSimpleAppCheckProviderFactory: NSObject, AppCheckProviderFactory {
  func createProvider(with app: FirebaseApp) -> AppCheckProvider? {
    return AppAttestProvider(app: app)
  }
}

Reproducing the issue

No response

Firebase SDK Version

10.19.1

Xcode Version

15.0

Installation Method

Swift Package Manager

Firebase Product(s)

App Check

Targeted Platforms

iOS

Relevant Log Output

Thread 7#0  (null) in CF_IS_OBJC ()
#1  (null) in CFStringGetLength ()
#2  (null) in firebase::firestore::util::MakeString(__CFString const*) ()
#3  (null) in invocation function for block in firebase::firestore::credentials::FirebaseAppCheckCredentialsProvider::GetToken(std::__1::function<void (firebase::firestore::util::StatusOr<std::__1::basic_string<... ()
#4  0x0000000104ca2970 in __49-[FIRAppCheck getTokenForcingRefresh:completion:]_block_invoke at /Users/john/Library/Developer/Xcode/DerivedData/LSI-durlhbcogjafnaambgywthbextfh/SourcePackages/checkouts/firebase-ios-sdk/FirebaseAppCheck/Sources/Core/FIRAppCheck.m:215
#5  0x0000000104cad064 in __46-[GACAppCheck tokenForcingRefresh:completion:]_block_invoke at /Users/john/Library/Developer/Xcode/DerivedData/LSI-durlhbcogjafnaambgywthbextfh/SourcePackages/checkouts/app-check/AppCheckCore/Sources/Core/GACAppCheck.m:115
#6  0x00000001046cfac8 in __56-[FBLPromise chainOnQueue:chainedFulfill:chainedReject:]_block_invoke.33 at /Users/john/Library/Developer/Xcode/DerivedData/LSI-durlhbcogjafnaambgywthbextfh/SourcePackages/checkouts/promises/Sources/FBLPromises/FBLPromise.m:273
#7  (null) in _dispatch_call_block_and_release ()
#8  (null) in _dispatch_client_callout ()
#9  (null) in _dispatch_queue_override_invoke ()
#10 (null) in _dispatch_root_queue_drain ()
#11 (null) in _dispatch_worker_thread2 ()
#12 (null) in _pthread_wqthread ()
#13 (null) in start_wqthread ()

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

<!--- Look below for instructions on how to share your Package.resolved. --->

<details>
<summary>Expand <code>Package.resolved</code> snippet</summary>
<br>

```json

{
  "pins" : [
    {
      "identity" : "abseil-cpp-binary",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/google/abseil-cpp-binary.git",
      "state" : {
        "revision" : "bfc0b6f81adc06ce5121eb23f628473638d67c5c",
        "version" : "1.2022062300.0"
      }
    },
    {
      "identity" : "algoliasearch-client-swift",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/algolia/algoliasearch-client-swift",
      "state" : {
        "revision" : "44f45e11ae53d473bb3cfd7d0359a0cfb9355dc3",
        "version" : "8.19.0"
      }
    },
    {
      "identity" : "app-check",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/google/app-check.git",
      "state" : {
        "revision" : "5746b2d35c91c50581590ed97abe4c06b5037274",
        "version" : "10.18.0"
      }
    },
    {
      "identity" : "appauth-ios",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/openid/AppAuth-iOS.git",
      "state" : {
        "revision" : "71cde449f13d453227e687458144bde372d30fc7",
        "version" : "1.6.2"
      }
    },
    {
      "identity" : "audiokit",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/AudioKit/AudioKit.git",
      "state" : {
        "revision" : "82cab9d4c168b5b03b67e2ff099819f589b530f7",
        "version" : "5.6.2"
      }
    },
    {
      "identity" : "differencekit",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/ra1028/DifferenceKit.git",
      "state" : {
        "revision" : "073b9671ce2b9b5b96398611427a1f929927e428",
        "version" : "1.3.0"
      }
    },
    {
      "identity" : "facebook-ios-sdk",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/facebook/facebook-ios-sdk.git",
      "state" : {
        "revision" : "7fd8a930a5b2c940a22efafe0e214ed0df671312",
        "version" : "15.1.0"
      }
    },
    {
      "identity" : "firebase-ios-sdk",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/firebase/firebase-ios-sdk.git",
      "state" : {
        "revision" : "c60c958e707c50a9cf8bcb7cfd7d51c566d726c5",
        "version" : "10.19.1"
      }
    },
    {
      "identity" : "firebaseui-ios",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/firebase/FirebaseUI-iOS.git",
      "state" : {
        "revision" : "a80bbc8fd3d27e097982ac44e66abe8b84b28ba3",
        "version" : "13.1.0"
      }
    },
    {
      "identity" : "giphy-ios-sdk",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/Giphy/giphy-ios-sdk",
      "state" : {
        "revision" : "95c32b862185e76107841b49bfff8ff7230f3b68",
        "version" : "2.2.7"
      }
    },
    {
      "identity" : "googleappmeasurement",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/google/GoogleAppMeasurement.git",
      "state" : {
        "revision" : "6b332152355c372ace9966d8ee76ed191f97025e",
        "version" : "10.17.0"
      }
    },
    {
      "identity" : "googledatatransport",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/google/GoogleDataTransport.git",
      "state" : {
        "revision" : "a732a4b47f59e4f725a2ea10f0c77e93a7131117",
        "version" : "9.3.0"
      }
    },
    {
      "identity" : "googlesignin-ios",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/google/GoogleSignIn-iOS",
      "state" : {
        "revision" : "9c9b36af86a4dd3da16048a36cf37351e63ccfe1",
        "version" : "6.2.4"
      }
    },
    {
      "identity" : "googleutilities",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/google/GoogleUtilities.git",
      "state" : {
        "revision" : "bc27fad73504f3d4af235de451f02ee22586ebd3",
        "version" : "7.12.1"
      }
    },
    {
      "identity" : "grpc-binary",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/google/grpc-binary.git",
      "state" : {
        "revision" : "a673bc2937fbe886dd1f99c401b01b6d977a9c98",
        "version" : "1.49.1"
      }
    },
    {
      "identity" : "gtm-session-fetcher",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/google/gtm-session-fetcher.git",
      "state" : {
        "revision" : "5ccda3981422a84186387dbb763ba739178b529c",
        "version" : "2.3.0"
      }
    },
    {
      "identity" : "gtmappauth",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/google/GTMAppAuth.git",
      "state" : {
        "revision" : "6dee0cde8a1b223737a5159e55e6b4ec16bbbdd9",
        "version" : "1.3.1"
      }
    },
    {
      "identity" : "hero",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/HeroTransitions/Hero",
      "state" : {
        "revision" : "66a554b21116fccf61b3aa2205a86f6b0cdc9766",
        "version" : "1.6.2"
      }
    },
    {
      "identity" : "instructions",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/ephread/Instructions.git",
      "state" : {
        "revision" : "54f50ec058a118107178f641f7475db353080c6d",
        "version" : "2.3.0"
      }
    },
    {
      "identity" : "interop-ios-for-google-sdks",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/google/interop-ios-for-google-sdks.git",
      "state" : {
        "revision" : "2d12673670417654f08f5f90fdd62926dc3a2648",
        "version" : "100.0.0"
      }
    },
    {
      "identity" : "leveldb",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/firebase/leveldb.git",
      "state" : {
        "revision" : "9d108e9112aa1d65ce508facf804674546116d9c",
        "version" : "1.22.3"
      }
    },
    {
      "identity" : "libphonenumber-ios",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/iziz/libPhoneNumber-iOS.git",
      "state" : {
        "revision" : "a6f5cb0a2a264e8396da65b0fb27ecf0df8a975e",
        "version" : "1.1.0"
      }
    },
    {
      "identity" : "libwebp-xcode",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/SDWebImage/libwebp-Xcode",
      "state" : {
        "revision" : "b2b1d20a90b14d11f6ef4241da6b81c1d3f171e4",
        "version" : "1.3.2"
      }
    },
    {
      "identity" : "lottie-ios",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/airbnb/lottie-ios.git",
      "state" : {
        "revision" : "3f1202f254ad5d71f6c5b518ed2d6b9e2abe6969",
        "version" : "4.3.4"
      }
    },
    {
      "identity" : "mapbox-common-ios",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/mapbox/mapbox-common-ios.git",
      "state" : {
        "revision" : "f9c766afbc69d2685f8a95157b0c1a177da20465",
        "version" : "23.8.5"
      }
    },
    {
      "identity" : "mapbox-core-maps-ios",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/mapbox/mapbox-core-maps-ios.git",
      "state" : {
        "revision" : "a7847acfef0c62e0a0135804f029e92f026b3c43",
        "version" : "10.16.3"
      }
    },
    {
      "identity" : "mapbox-maps-ios",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/mapbox/mapbox-maps-ios.git",
      "state" : {
        "revision" : "1678e9d0cc1d462d2b97473da685464009908b6a",
        "version" : "10.16.3"
      }
    },
    {
      "identity" : "mixpanel-swift",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/mixpanel/mixpanel-swift",
      "state" : {
        "revision" : "cf569f790c0ec9e9f94f25e56b7ba5762198d34a",
        "version" : "2.10.4"
      }
    },
    {
      "identity" : "nanopb",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/firebase/nanopb.git",
      "state" : {
        "revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692",
        "version" : "2.30909.0"
      }
    },
    {
      "identity" : "phonenumberkit",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/marmelroy/PhoneNumberKit.git",
      "state" : {
        "revision" : "d2886b0735a47e47fb227504666756efb2e2ac26",
        "version" : "3.7.6"
      }
    },
    {
      "identity" : "promises",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/google/promises.git",
      "state" : {
        "revision" : "e70e889c0196c76d22759eb50d6a0270ca9f1d9e",
        "version" : "2.3.1"
      }
    },
    {
      "identity" : "realm-core",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/realm/realm-core.git",
      "state" : {
        "revision" : "7227d6a447821c28895daa099b6c7cd4c99d461b",
        "version" : "13.25.1"
      }
    },
    {
      "identity" : "realm-swift",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/realm/realm-swift.git",
      "state" : {
        "branch" : "master",
        "revision" : "72a0e6a09c6f355eb760cfdaeebacf612c3ac211"
      }
    },
    {
      "identity" : "sdwebimage",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/SDWebImage/SDWebImage.git",
      "state" : {
        "revision" : "fd010e54231331fc19338f81c6d072cd9ace2825",
        "version" : "5.18.8"
      }
    },
    {
      "identity" : "smile",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/onmyway133/Smile.git",
      "state" : {
        "revision" : "40604722a7a56f735124e069fcbb58307637744b",
        "version" : "2.1.0"
      }
    },
    {
      "identity" : "swift-log",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/apple/swift-log.git",
      "state" : {
        "revision" : "532d8b529501fb73a2455b179e0bbb6d49b652ed",
        "version" : "1.5.3"
      }
    },
    {
      "identity" : "swift-protobuf",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/apple/swift-protobuf.git",
      "state" : {
        "revision" : "65e8f29b2d63c4e38e736b25c27b83e012159be8",
        "version" : "1.25.2"
      }
    },
    {
      "identity" : "tocropviewcontroller",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/TimOliver/TOCropViewController.git",
      "state" : {
        "revision" : "d0470491f56e734731bbf77991944c0dfdee3e0e",
        "version" : "2.6.1"
      }
    },
    {
      "identity" : "turf-swift",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/mapbox/turf-swift.git",
      "state" : {
        "revision" : "f0afe204b4266337066a436becab62fdd2b32378",
        "version" : "2.7.0"
      }
    },
    {
      "identity" : "wave",
      "kind" : "remoteSourceControl",
      "location" : "https://github.com/jtrivedi/Wave",
      "state" : {
        "revision" : "d95ce41fa52c42d9790388e9086c892930e9e48b",
        "version" : "0.3.2"
      }
    }
  ],
  "version" : 2
}

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 9 months ago

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

paulb777 commented 9 months ago

Thanks for the report @johnfrancmartin

It looks like AppCheck and Firestore have different expectations about tokens and error handling for the tokenForcingRefresh API.

FirebaseAppCheck returns nil when there's an error : https://github.com/firebase/firebase-ios-sdk/blob/main/FirebaseAppCheck/Sources/Core/FIRAppCheck.m#L156

but Firestore still expects to return a token after an error: https://github.com/firebase/firebase-ios-sdk/blob/main/Firestore/core/src/credentials/firebase_app_check_credentials_provider_apple.mm#L88

@andrewheard Would appreciate any insights you have.

andrewheard commented 9 months ago

@paulb777 Firestore is actually calling the interop API getTokenForcingRefresh:completion:, not the public API tokenForcingRefresh:completion:. The interop API should always return the placeholder token if there's an error, never nil.

So far I've made a sample app with the following:

import FirebaseAppCheck
import FirebaseAppCheckInterop
import FirebaseCore
import SwiftUI

class AppAttestProviderFactory: NSObject, AppCheckProviderFactory {
  func createProvider(with app: FirebaseApp) -> AppCheckProvider? {
    return AppAttestProvider(app: app)
  }
}

class AppDelegate: NSObject, UIApplicationDelegate {
  func application(_ application: UIApplication,
                   didFinishLaunchingWithOptions launchOptions: [UIApplication
                     .LaunchOptionsKey: Any]? = nil) -> Bool {
    AppCheck.setAppCheckProviderFactory(AppAttestProviderFactory())
    FirebaseApp.configure()

    guard let appCheckInterop = AppCheck.appCheck() as? AppCheckInterop else {
      print("AppCheckInterop instance was nil.")
      return false
    }

    appCheckInterop.getToken(forcingRefresh: true) { tokenResult in
      print("AppCheckInterop: \(tokenResult.token)")
      // prints "AppCheckInterop: eyJlcnJvciI6IlVOS05PV05fRVJST1IifQ=="

      if let error = tokenResult.error {
        print("AppCheckInterop Error: \(error)")
        // prints "Error Domain=com.google.app_check_core Code=4 "The attestation provider
        // AppAttestProvider is not supported on current platform and OS version. ..."
      }
    }

    return true
  }
}

The AppCheckInterop API appears to be behaving as expected, returning both the placeholder token and an error. I'll try introducing Firestore next to see if I can reproduce there.

andrewheard commented 9 months ago

@paulb777 I added Firestore and the code below worked:

let db = Firestore.firestore()
let citiesRef = db.collection("cities")
citiesRef.addDocument(data: ["name": "Waterloo", "country": "Canada"])

The following was printed to the console (no crash):

[FirebaseFirestore][I-FST000001] AppCheck failed: 'The operation couldn’t be completed. The attestation provider AppAttestProvider is not supported on current platform and OS version.'

@johnfrancmartin Is this a new app or did the issue appear when you upgraded to Firebase 10.19.1 from an earlier version? Do you happen to have a minimal sample app that you could share that reproduces the issue? Unfortunately I'm not able to reproduce it on my end.

google-oss-bot commented 8 months ago

Hey @johnfrancmartin. We need more information to resolve this issue but there hasn't been an update in 5 weekdays. I'm marking the issue as stale and if there are no new updates in the next 5 days I will close it automatically.

If you have more information that will help us get to the bottom of this, just add a comment!

johnfrancmartin commented 8 months ago

Hey, apologies I was OOO.

Not a new app, I believe the earliest version something like this appeared in was around October of last year, and AppCheck was only added in September, so I expect it's not version related. I can't currently repro myself, so don't believe I yet know how to make a minimal sample app for this.

Don't know if it's helpful, but got another crash today:

Crashed: com.apple.root.default-qos
0  libsystem_kernel.dylib         0x9fbc __pthread_kill + 8
1  libsystem_pthread.dylib        0x5680 pthread_kill + 268
2  libsystem_c.dylib              0x75b90 abort + 180
3  libsystem_malloc.dylib         0x26200 malloc_vreport + 908
4  libsystem_malloc.dylib         0x264a8 malloc_zone_error + 104
5  libsystem_malloc.dylib         0x6a90 _szone_free + 480
6  CoreFoundation                 0x1226c __CFBasicHashRehash + 1476
7  CoreFoundation                 0x119a8 __CFBasicHashAddValue + 100
8  CoreFoundation                 0x2c90 CFDictionarySetValue + 208
9  CFNetwork                      0x20972c _CFHTTPServerResponseEnqueue + 25072
10 CFNetwork                      0xdf9b8 _CFStreamErrorFromCFError + 9644
11 CFNetwork                      0x211c CFURLRequestSetHTTPHeaderFieldValue + 136
12 Throwback                      0x48ad88 closure #2 in StorageTokenAuthorizer.authorizeRequest(_:completionHandler:) + 64 (StorageTokenAuthorizer.swift:64)
13 Throwback                      0x48af18 thunk for @escaping @callee_guaranteed (@guaranteed FIRAppCheckTokenResultInterop) -> () + 4381126424 (<compiler-generated>:4381126424)
14 Throwback                      0x37cdc0 __49-[FIRAppCheck getTokenForcingRefresh:completion:]_block_invoke + 216 (FIRAppCheck.m:216)
15 Throwback                      0x3874b4 __46-[GACAppCheck tokenForcingRefresh:completion:]_block_invoke + 115 (GACAppCheck.m:115)
16 Throwback                      0xe8e98 __56-[FBLPromise chainOnQueue:chainedFulfill:chainedReject:]_block_invoke.33 + 273 (FBLPromise.m:273)
17 libdispatch.dylib              0x26a8 _dispatch_call_block_and_release + 32
18 libdispatch.dylib              0x4300 _dispatch_client_callout + 20
19 libdispatch.dylib              0x74cc _dispatch_queue_override_invoke + 1056
20 libdispatch.dylib              0x15be4 _dispatch_root_queue_drain + 392
21 libdispatch.dylib              0x163ec _dispatch_worker_thread2 + 156
22 libsystem_pthread.dylib        0x1928 _pthread_wqthread + 228
23 libsystem_pthread.dylib        0x1a04 start_wqthread + 8
johnfrancmartin commented 4 months ago

Wanted to check-in, this issue is still going on:

          Crashed: com.apple.root.default-qos
0  libsystem_kernel.dylib         0xc42c __pthread_kill + 8
1  libsystem_pthread.dylib        0x7c0c pthread_kill + 268
2  libsystem_c.dylib              0x75ba0 abort + 180
3  libsystem_malloc.dylib         0x9588 malloc_vreport + 896
4  libsystem_malloc.dylib         0x6430 malloc_zone_error + 104
5  libsystem_malloc.dylib         0x1fd4 _szone_free + 480
6  CoreFoundation                 0x19464 __CFBasicHashRehash + 1476
7  CoreFoundation                 0x85dc __CFBasicHashAddValue + 100
8  CoreFoundation                 0x2a2e4 CFDictionarySetValue + 208
9  CFNetwork                      0x9028 CFURLResponseGetExpectedContentLength + 644
10 CFNetwork                      0x8e98 CFURLResponseGetExpectedContentLength + 244
11 CFNetwork                      0x8240 CFURLRequestSetHTTPHeaderFieldValue + 136
12 Throwback                      0x4e042c closure #2 in StorageTokenAuthorizer.authorizeRequest(_:completionHandler:) + 64 (StorageTokenAuthorizer.swift:64)
13 Throwback                      0x4e05bc thunk for @escaping @callee_guaranteed (@guaranteed FIRAppCheckTokenResultInterop) -> () + 4380886460 (<compiler-generated>:4380886460)
14 Throwback                      0x3d24c4 __49-[FIRAppCheck getTokenForcingRefresh:completion:]_block_invoke + 216 (FIRAppCheck.m:216)
15 Throwback                      0x3dcbb8 __46-[GACAppCheck tokenForcingRefresh:completion:]_block_invoke + 115 (GACAppCheck.m:115)
16 Throwback                      0x13b214 __56-[FBLPromise chainOnQueue:chainedFulfill:chainedReject:]_block_invoke.33 + 273 (FBLPromise.m:273)
17 libdispatch.dylib              0x213c _dispatch_call_block_and_release + 32
18 libdispatch.dylib              0x3dd4 _dispatch_client_callout + 20
19 libdispatch.dylib              0x6fec _dispatch_queue_override_invoke + 1056
20 libdispatch.dylib              0x15894 _dispatch_root_queue_drain + 392
21 libdispatch.dylib              0x1609c _dispatch_worker_thread2 + 156
22 libsystem_pthread.dylib        0x48f8 _pthread_wqthread + 228
23 libsystem_pthread.dylib        0x10cc start_wqthread + 8
paulb777 commented 4 months ago

Adding this issue for investigation for 10.29.0

Open Questions:

andrewheard commented 4 months ago

@paulb777 In App Check we're trying to differentiate between 3 states with the header:

  1. App Check is included in the app and producing valid tokens (header contains valid token).
  2. App Check is included in the app but not producing valid tokens, either due to a misconfiguration, malicious actor or some other failure (header contains placeholderToken).
  3. App Check is not included in the app, potentially due to a user using an older version of the app before App Check was integrated or a malicious actor calling APIs directly (header not included).

In other words, the placeholder token allows developers to see what percentage of their user base has upgraded to a version of their app that includes App Check before switching from unenforced (monitoring) mode to enforced (to avoid prematurely blocking legitimate users before they've upgraded).

I don't think Auth has a similar concept, which is why it was only done for App Check.

paulb777 commented 4 months ago

We published AppCheckCore 10.19.2 for CocoaPods and Swift Package Manager that may fix this issue. We were never able to reproduce, so please update and let us know.