Firebase AppCheck on iOS: 403 permission errors - PERMISSION_DENIED
Asked Answered
C

4

10

Q: How can permission errors be resolved for Firebase App Check?

Background: I have enabled App Check per the documents:

DeviceCheck is enabled/configured per: https://firebase.google.com/docs/app-check/ios/devicecheck-provider

App Attest is enabled configured per: https://firebase.google.com/docs/app-check/ios/devicecheck-provider

SDK is added to the project, with code from: https://github.com/firebase/firebase-ios-sdk/blob/master/FirebaseAppCheck/Apps/FIRAppCheckTestApp/FIRAppCheckTestApp/AppDelegate.swift

Specifically, in appdelegate: Token setup:

FirebaseApp.configure()

requestDeviceCheckToken()

requestDebugToken()

if #available(iOS 14.0, *) {
  requestAppAttestToken()
}

calling:

  // MARK: App Check providers
  func requestDeviceCheckToken() {
    guard let firebaseApp = FirebaseApp.app() else {
      return
    }

    DeviceCheckProvider(app: firebaseApp)?.getToken { token, error in
      if let token = token {
        print("DeviceCheck token: \(token.token), expiration date: \(token.expirationDate)")
      }

      if let error = error {
        print("DeviceCheck error: \((error as NSError).userInfo)")
      }
    }
  }

  func requestDebugToken() {
    guard let firebaseApp = FirebaseApp.app() else {
      return
    }

    if let debugProvider = AppCheckDebugProvider(app: firebaseApp) {
      print("Debug token: \(debugProvider.currentDebugToken())")

      debugProvider.getToken { token, error in
        if let token = token {
          print("Debug FAC token: \(token.token), expiration date: \(token.expirationDate)")
        }

        if let error = error {
          print("Debug error: \(error)")
        }
      }
    }
  }

  @available(iOS 14.0, *)
  func requestAppAttestToken() {
    guard let firebaseApp = FirebaseApp.app() else {
      return
    }

    guard let appAttestProvider = AppAttestProvider(app: firebaseApp) else {
      print("Failed to instantiate AppAttestProvider")
      return
    }

    appAttestProvider.getToken { token, error in
      if let token = token {
        print("App Attest FAC token: \(token.token), expiration date: \(token.expirationDate)")
      }

      if let error = error {
        print("App Attest error: \(error)")
      }
    }
  }

requestDeviceCheckToken()returns a permissions error:

DeviceCheck error: ["NSLocalizedFailureReason": The server responded with an error: 
 - URL: https://firebaseappcheck.googleapis.com/v1beta/projects/<GOOGLE_APP_ID>:exchangeDeviceCheckToken 
 - HTTP status code: 403 
 - Response body: {
  "error": {
    "code": 403,
    "message": "Requests from this iOS client application \u003cempty\u003e are blocked.",
    "status": "PERMISSION_DENIED",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "API_KEY_IOS_APP_BLOCKED",
        "domain": "googleapis.com",
        "metadata": {
          "service": "firebaseappcheck.googleapis.com",
          "consumer": "projects/<my project #>"
        }
      }
    ]
  }
}

requestDebugToken() returns a permissions error:

Debug error: Error Domain=com.firebase.appCheck Code=0 "The server responded with an error: 
 - URL: https://firebaseappcheck.googleapis.com/v1beta/projects/<GOOGLE_APP_ID>:exchangeDebugToken 
 - HTTP status code: 403 
 - Response body: {
  "error": {
    "code": 403,
    "message": "Requests from this iOS client application \u003cempty\u003e are blocked.",
    "status": "PERMISSION_DENIED",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "API_KEY_IOS_APP_BLOCKED",
        "domain": "googleapis.com",
        "metadata": {
          "consumer": "projects/<my project #>",
          "service": "firebaseappcheck.googleapis.com"
        }
      }
    ]
  }
}
" UserInfo={NSLocalizedFailureReason=The server responded with an error: 
 - URL: https://firebaseappcheck.googleapis.com/v1beta/projects/<GOOGLE_APP_ID>:exchangeDebugToken 
 - HTTP status code: 403 
 - Response body: {
  "error": {
    "code": 403,
    "message": "Requests from this iOS client application \u003cempty\u003e are blocked.",
    "status": "PERMISSION_DENIED",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "API_KEY_IOS_APP_BLOCKED",
        "domain": "googleapis.com",
        "metadata": {
          "consumer": "projects/<my project #",
          "service": "firebaseappcheck.googleapis.com"
        }
      }
    ]
  }
}
}

requestAppAttestToken() returns an error:

App Attest error: Error Domain=com.firebase.appCheck Code=0 "(null)"

GCP Console does show all calls to the following w/ 100% errors:

google.firebase.appcheck.v1beta.TokenExchangeService.ExchangeDebugToken 
    google.firebase.appcheck.v1beta.TokenExchangeService.ExchangeDeviceCheckToken   
    google.firebase.appcheck.v1beta.TokenExchangeService.GenerateAppAttestChallenge 

All of which seem to point to a permissions error? Specifically, GOOGLE_APP_ID is in the request URL, but App Check is configured in Firebase via the console...

I'm not seeing anything in the docs or anything obvious in IAM that I missed? :(

Ty in advance for help!

Update After further testing w/ Postman:

The issue seems to be that the SDK isn't passing the X-Ios-Bundle-Identifier correctly when calling AppCheck API(s).

Steps to get to this conclusion:

  • From POSTMAN: API call w/ original API_KEY -> yields initial (above) error response/403
  • From POSTMAN: API call as above, + X-Ios-Bundle-Identifier + valid debug_token -> yields success payload.

So:

  • any ideas to help ID why the X-Ios-Bundle-Identifier isn't being passed by the SDK? The app is using other Firebase API's w/out issue, so seems limited to the AppCheck SDK...
  • and/or - can the X-Ios-Bundle-Identifier be programmatically added (in Swift) to the AppCheck calls (it is properly notated in the .plist)

Resolved!

App Check SDK does not currently support the Android / iOS Application Restriction for API keys. With that, you must remove the App Restriction for your API keys to resolve this issue.

Hopefully, the Application Restriction(s) will be supported at some point...

Update!

v8.8.0-beta now supports the bundle ID! :)

Cythiacyto answered 2/9, 2021 at 17:42 Comment(1)
where do I remove the App Restriction for your API keys?Upland
K
6

1. Configure the private key for DeviceCheck

  • Make sure you have created a private key for DeviceCheck enter image description here

  • And installed it in firebase project settings under AppCheck tab enter image description here

https://firebase.google.com/docs/app-check/ios/devicecheck-provider

2. Add debug token to firebase.

If you use AppCheckDebugProvider (basically for simulators), after run the project you will see a debug token in the console, you need to copy it and add to AppCheck of the project settings. Than AppCheck will approve it. Also don't forget to add -FIRDebugEnabled for the Arguments Passed on Launch.

https://firebase.google.com/docs/app-check/ios/debug-provider

3. Add production entitlements for AppAttest environment.

The beta version of AppCheck doesn't work with the AppAttest development environment, so you need to setup the production environment in your entitlements. By default, AppAttest works in a development environment, and regardless of your choice in the market it will work with a production.

https://firebase.google.com/docs/app-check/ios/app-attest-provider

https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_devicecheck_appattest-environment

enter image description here

4. Optional:

You can simplify the code

#if targetEnvironment (simulator)
    let providerFactory = AppCheckDebugProviderFactory ()
#else
    let providerFactory = CustomAppCheckProviderFactory ()
#endif

AppCheck.setAppCheckProviderFactory (providerFactory)

And getting a token

if let fbApp = FirebaseApp.app () {
    providerFactory.createProvider(with: fbApp)?.getToken { token, error in
        if let token = token {
            print ("AppCheck token: \ (token.token), expiration date: \ (token.expirationDate)")
        } else if let error = error {
            print ("AppCheck error: \ (error as NSError).userInfo)")
        }
    }
}

Or if you want to protect non-firebase resources, you can get a token like this:

AppCheck.appCheck().token (forcingRefresh: false) { token, error in
    if let token = token {
        print ("AppCheck token: \ (token.token), expiration date: \ (token.expirationDate)")
    } else if let error = error {
        print ("AppCheck error: \ (error as NSError).userInfo)")
    }
}

https://firebase.google.com/docs/app-check/ios/custom-resource

Kagoshima answered 5/9, 2021 at 9:46 Comment(6)
I've already configured the private key for DeviceCheck, added the debug token to Firebase, and added the production entitlements for AppAttest environment - that isn't the issue here. Per the original post, the issue Is surrounding permission settings on the cal to the FIrebase AppCheck API -Cythiacyto
Check app ID in GoogleServices-info.plist and firebase project settings, are they the same? When I changed my app ID in .plist I got the same error.Kagoshima
You should probably update GoogleService-Info.plist file.Kagoshima
TY for all of your suggestions. I did go ahead and update the GoogleService-Info.plist, but same result. The issue seems to be that the SDK - FirebaseAppCheck (8.6.0-beta) - isn't passing the X-Ios-Bundle-Identifier correctly when calling AppCheck API(s). Only for AppCheck tho, the other SDK calls function correctly...Cythiacyto
Thanks GoSti. Your guide is so helpful. I have it working in debug mode but I'm stuck at #3 I can't find the "entitlements" file nor the "App Attest Environment" key. I checked my info.plist and "Build Settings" without luck. I want to change it to "production" Thanks again!Drift
It looks like there are currently no capabilities in your project. Xcode will automatically generate the Entitlements file when you add a capability.Kagoshima
C
4

App Check SDK does not currently support the Android / iOS Application Restriction for API keys. With that, you must remove the App Restriction for your API keys to resolve this issue.

Hopefully, the Application Restriction(s) will be supported at some point...

Cythiacyto answered 20/9, 2021 at 2:24 Comment(5)
Thanks for this answer. Now I've looked on the firebase console and in the generated GoogleService-Info.plist, but I don't see any reference to "Application Restrictions". Perhaps I don't have permission to see or change them?Headrest
@Headrest - Restriction settings are on the API Key you're using, API keys are in the APIs & Services > Credentials panel in the Google Cloud Console (not in the plist; nor the Firebase Console). console.cloud.google.com/apis/credentialsCythiacyto
Aha, thanks for the additional information!Headrest
@Cythiacyto Where did you find this information? My API Key for Android app covers some Google APIs, but not the App Check API. Still, I get this error: Error returned from API. code: 403 body: Requests to this API firebaseappcheck.googleapis.com method google.firebase.appcheck.v1beta.TokenExchangeService.ExchangeDebugToken are blocked.Coot
I worked with Google on it. It's supported now though, since Version 8.8.0 - September 28, 2021...Cythiacyto
G
1

Got this error - here is what worked for me:

  • Run app on real Android device
  • Opened Android Studio → Logcat → search for “DebugAppCheckProvider” → copy the debug secret
  • In Firebase Go to “App Check” → Apps → 3 dot menu → Manage debug token → Add token → name it → paste the debug secret
  • Add console log of the token after activation of app check.
  try {
      await firebase.appCheck().activate("ignored", true);
      const token = await getAppCheckToken();
      console.log({ token });
   } catch (err) {      
      console.error(err);
   }
};
Gladine answered 28/11, 2022 at 13:24 Comment(0)
I
0

It could be that you didn't enable App Attest in your capabilities:

enter image description here

in Identifiers

Don't forget to add the same capability in your Xcode:

enter image description here

Go to "Targets" -> Your target -> Signing & Capabilities -> press "Add capability" and choose "App Attest".

Isaisaac answered 28/5, 2024 at 0:31 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.