title: Firebase AppCheck on iOS: 403 permission errors - PERMISSION_DENIED
body: <p>Q:
  How can permission errors be resolved for Firebase App Check?</p>
  <p>Background:
  I have enabled App Check per the documents:</p>
  <p>DeviceCheck is enabled/configured per:
  <a href="https://firebase.google.com/docs/app-check/ios/devicecheck-provider" rel="nofollow noreferrer">https://firebase.google.com/docs/app-check/ios/devicecheck-provider</a></p>
  <p>App Attest is enabled configured per:
  <a href="https://firebase.google.com/docs/app-check/ios/devicecheck-provider" rel="nofollow noreferrer">https://firebase.google.com/docs/app-check/ios/devicecheck-provider</a></p>
  <p>SDK is added to the project, with code from:
  <a href="https://github.com/firebase/firebase-ios-sdk/blob/master/FirebaseAppCheck/Apps/FIRAppCheckTestApp/FIRAppCheckTestApp/AppDelegate.swift" rel="nofollow noreferrer">https://github.com/firebase/firebase-ios-sdk/blob/master/FirebaseAppCheck/Apps/FIRAppCheckTestApp/FIRAppCheckTestApp/AppDelegate.swift</a></p>
  <p>Specifically, in appdelegate:
  Token setup:</p>
  <pre><code>FirebaseApp.configure()
  
  requestDeviceCheckToken()
  
  requestDebugToken()
  
  if #available(iOS 14.0, *) {
    requestAppAttestToken()
  }
  </code></pre>
  <p>calling:</p>
  <pre><code>  // 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(&quot;DeviceCheck token: \(token.token), expiration date: \(token.expirationDate)&quot;)
        }
  
        if let error = error {
          print(&quot;DeviceCheck error: \((error as NSError).userInfo)&quot;)
        }
      }
    }
  
    func requestDebugToken() {
      guard let firebaseApp = FirebaseApp.app() else {
        return
      }
  
      if let debugProvider = AppCheckDebugProvider(app: firebaseApp) {
        print(&quot;Debug token: \(debugProvider.currentDebugToken())&quot;)
  
        debugProvider.getToken { token, error in
          if let token = token {
            print(&quot;Debug FAC token: \(token.token), expiration date: \(token.expirationDate)&quot;)
          }
  
          if let error = error {
            print(&quot;Debug error: \(error)&quot;)
          }
        }
      }
    }
  
    @available(iOS 14.0, *)
    func requestAppAttestToken() {
      guard let firebaseApp = FirebaseApp.app() else {
        return
      }
  
      guard let appAttestProvider = AppAttestProvider(app: firebaseApp) else {
        print(&quot;Failed to instantiate AppAttestProvider&quot;)
        return
      }
  
      appAttestProvider.getToken { token, error in
        if let token = token {
          print(&quot;App Attest FAC token: \(token.token), expiration date: \(token.expirationDate)&quot;)
        }
  
        if let error = error {
          print(&quot;App Attest error: \(error)&quot;)
        }
      }
    }
  </code></pre>
  <p>requestDeviceCheckToken()returns a permissions error:</p>
  <pre><code>DeviceCheck error: [&quot;NSLocalizedFailureReason&quot;: The server responded with an error: 
   - URL: https://firebaseappcheck.googleapis.com/v1beta/projects/&lt;GOOGLE_APP_ID&gt;:exchangeDeviceCheckToken 
   - HTTP status code: 403 
   - Response body: {
    &quot;error&quot;: {
      &quot;code&quot;: 403,
      &quot;message&quot;: &quot;Requests from this iOS client application \u003cempty\u003e are blocked.&quot;,
      &quot;status&quot;: &quot;PERMISSION_DENIED&quot;,
      &quot;details&quot;: [
        {
          &quot;@type&quot;: &quot;type.googleapis.com/google.rpc.ErrorInfo&quot;,
          &quot;reason&quot;: &quot;API_KEY_IOS_APP_BLOCKED&quot;,
          &quot;domain&quot;: &quot;googleapis.com&quot;,
          &quot;metadata&quot;: {
            &quot;service&quot;: &quot;firebaseappcheck.googleapis.com&quot;,
            &quot;consumer&quot;: &quot;projects/&lt;my project #&gt;&quot;
          }
        }
      ]
    }
  }
  </code></pre>
  <p>requestDebugToken() returns a permissions error:</p>
  <pre><code>Debug error: Error Domain=com.firebase.appCheck Code=0 &quot;The server responded with an error: 
   - URL: https://firebaseappcheck.googleapis.com/v1beta/projects/&lt;GOOGLE_APP_ID&gt;:exchangeDebugToken 
   - HTTP status code: 403 
   - Response body: {
    &quot;error&quot;: {
      &quot;code&quot;: 403,
      &quot;message&quot;: &quot;Requests from this iOS client application \u003cempty\u003e are blocked.&quot;,
      &quot;status&quot;: &quot;PERMISSION_DENIED&quot;,
      &quot;details&quot;: [
        {
          &quot;@type&quot;: &quot;type.googleapis.com/google.rpc.ErrorInfo&quot;,
          &quot;reason&quot;: &quot;API_KEY_IOS_APP_BLOCKED&quot;,
          &quot;domain&quot;: &quot;googleapis.com&quot;,
          &quot;metadata&quot;: {
            &quot;consumer&quot;: &quot;projects/&lt;my project #&gt;&quot;,
            &quot;service&quot;: &quot;firebaseappcheck.googleapis.com&quot;
          }
        }
      ]
    }
  }
  &quot; UserInfo={NSLocalizedFailureReason=The server responded with an error: 
   - URL: https://firebaseappcheck.googleapis.com/v1beta/projects/&lt;GOOGLE_APP_ID&gt;:exchangeDebugToken 
   - HTTP status code: 403 
   - Response body: {
    &quot;error&quot;: {
      &quot;code&quot;: 403,
      &quot;message&quot;: &quot;Requests from this iOS client application \u003cempty\u003e are blocked.&quot;,
      &quot;status&quot;: &quot;PERMISSION_DENIED&quot;,
      &quot;details&quot;: [
        {
          &quot;@type&quot;: &quot;type.googleapis.com/google.rpc.ErrorInfo&quot;,
          &quot;reason&quot;: &quot;API_KEY_IOS_APP_BLOCKED&quot;,
          &quot;domain&quot;: &quot;googleapis.com&quot;,
          &quot;metadata&quot;: {
            &quot;consumer&quot;: &quot;projects/&lt;my project #&quot;,
            &quot;service&quot;: &quot;firebaseappcheck.googleapis.com&quot;
          }
        }
      ]
    }
  }
  }
  </code></pre>
  <p>requestAppAttestToken() returns an error:</p>
  <pre><code>App Attest error: Error Domain=com.firebase.appCheck Code=0 &quot;(null)&quot;
  </code></pre>
  <p>GCP Console does show all calls to the following w/ 100% errors:</p>
  <pre><code>google.firebase.appcheck.v1beta.TokenExchangeService.ExchangeDebugToken 
      google.firebase.appcheck.v1beta.TokenExchangeService.ExchangeDeviceCheckToken   
      google.firebase.appcheck.v1beta.TokenExchangeService.GenerateAppAttestChallenge 
  </code></pre>
  <p>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...</p>
  <p>I'm not seeing anything in the docs or anything obvious in IAM that I missed?  :(</p>
  <p>Ty in advance for help!</p>
  <p><em><strong><strong>Update</strong></strong></em>
  After further testing w/ Postman:</p>
  <p><em><strong>The issue seems to be that the SDK isn't passing the <code>X-Ios-Bundle-Identifier</code> correctly when calling AppCheck API(s).</strong></em></p>
  <p>Steps to get to this conclusion:</p>
  <ul>
  <li>From POSTMAN: API call w/ original API_KEY -&gt; yields initial (above) error response/403</li>
  <li>From POSTMAN: API call as above, + X-Ios-Bundle-Identifier + valid debug_token -&gt; yields success payload.</li>
  </ul>
  <p>So:</p>
  <ul>
  <li>any ideas to help ID why the <code>X-Ios-Bundle-Identifier</code> 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...</li>
  <li>and/or - can the <code>X-Ios-Bundle-Identifier</code> be programmatically added (in Swift) to the AppCheck calls (it is properly notated in the .plist)</li>
  </ul>
  <p><strong>Resolved!</strong></p>
  <p>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.</p>
  <p>Hopefully, the Application Restriction(s) will be supported at some point...</p>
  <p><strong>Update!</strong></p>
  <p>v8.8.0-beta now supports the bundle ID!  :)</p>