【问题标题】:Firebase AppCheck on iOS: 403 permission errors - PERMISSION_DENIEDiOS 上的 Firebase AppCheck:403 权限错误 - PERMISSION_DENIED
【发布时间】:2021-09-02 17:42:30
【问题描述】:

问: 如何解决 Firebase App Check 的权限错误?

背景: 我已根据文档启用 App Check:

DeviceCheck 启用/配置如下: https://firebase.google.com/docs/app-check/ios/devicecheck-provider

App Attest 启用配置如下: https://firebase.google.com/docs/app-check/ios/devicecheck-provider

SDK 被添加到项目中,代码来自: https://github.com/firebase/firebase-ios-sdk/blob/master/FirebaseAppCheck/Apps/FIRAppCheckTestApp/FIRAppCheckTestApp/AppDelegate.swift

具体来说,在 appdelegate 中: 令牌设置:

FirebaseApp.configure()

requestDeviceCheckToken()

requestDebugToken()

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

调用:

  // 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() 返回权限错误:

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() 返回权限错误:

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() 返回错误:

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

GCP Console 确实显示了对以下 100% 错误的所有调用:

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

所有这些似乎都指向权限错误?具体来说,GOOGLE_APP_ID 在请求 URL 中,但 App Check 是通过控制台在 Firebase 中配置的...

我没有在文档中看到任何我错过的内容或在 IAM 中看到的任何明显内容? :(

提前求助!

更新 在使用 Postman 进行进一步测试后:

问题似乎是 SDK 在调用 AppCheck API 时未正确传递 X-Ios-Bundle-Identifier

得出这个结论的步骤:

  • 来自 POSTMAN:带有原始 API_KEY 的 API 调用 -> 产生初始(以上)错误响应/403
  • 来自 POSTMAN:API 调用如上,+ X-Ios-Bundle-Identifier + 有效 debug_token -> 产生成功负载。

所以:

  • 有什么想法可以帮助识别为什么 SDK 没有传递 X-Ios-Bundle-Identifier?该应用正在使用其他 Firebase API 的无问题,因此似乎仅限于 AppCheck SDK...
  • 和/或 - X-Ios-Bundle-Identifier 是否可以通过编程方式(在 Swift 中)添加到 AppCheck 调用中(在 .plist 中已正确注明)

已解决!

App Check SDK 目前不支持 API 密钥的 Android / iOS 应用程序限制。这样,您必须删除 API 密钥的应用限制才能解决此问题。

希望应用程序限制会在某个时候得到支持...

更新!

v8.8.0-beta 现在支持捆绑 ID! :)

【问题讨论】:

    标签: ios firebase firebase-app-check


    【解决方案1】:

    1.为 DeviceCheck 配置私钥

    • 确保您已为 DeviceCheck 创建了私钥

    • 并将其安装在 AppCheck 选项卡下的 firebase 项目设置中

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

    2。将调试令牌添加到 firebase。

    如果你使用 AppCheckDebugProvider(基本上是用于模拟器),运行项目后你会在控制台中看到一个调试令牌,你需要将它复制并添加到项目设置的 AppCheck 中。比 AppCheck 会批准它。另外不要忘记为Arguments Passed on Launch 添加-FIRDebugEnabled

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

    3.为 AppAttest 环境添加生产权利。

    AppCheck 测试版不适用于 AppAttest 开发环境,因此您需要在您的权利中设置 production 环境。默认情况下,AppAttest 在开发环境中工作,无论您在市场上做出何种选择,它都可以在生产环境中工作。

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

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

    4.可选:

    你可以简化代码

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

    并获得令牌

    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)")
            }
        }
    }
    

    或者如果你想保护非firebase资源,你可以得到这样的令牌:

    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

    【讨论】:

    • 我已经为 DeviceCheck 配置了私钥,将调试令牌添加到 Firebase,并为 AppAttest 环境添加了生产权利——这不是这里的问题。根据原始帖子,问题是围绕 FIrebase AppCheck API 的 cal 权限设置 -
    • 检查 GoogleServices-info.plist 和 firebase 项目设置中的应用 ID,它们是否相同?当我在 .plist 中更改我的应用 ID 时,我遇到了同样的错误。
    • 您可能应该更新 GoogleService-Info.plist 文件。
    • TY 为您提供所有建议。我确实继续更新了 GoogleService-Info.plist,但结果相同。问题似乎是 SDK - FirebaseAppCheck (8.6.0-beta) - 在调用 AppCheck API 时没有正确传递 X-Ios-Bundle-Identifier。仅对于 AppCheck tho,其他 SDK 调用功能正常...
    【解决方案2】:

    App Check SDK 目前不支持 API 密钥的 Android / iOS 应用程序限制。这样,您必须删除 API 密钥的应用限制才能解决此问题。

    希望应用程序限制会在某个时候得到支持...

    【讨论】:

    • 感谢您的回答。现在我查看了 firebase 控制台和生成的 GoogleService-Info.plist,但我没有看到对“应用程序限制”的任何引用。也许我无权查看或更改它们?
    • @axello - 限制设置位于您正在使用的 API 密钥上,API 密钥位于 Google Cloud Console 的 API 和服务 > 凭据面板中(不在 plist 中;也不在 Firebase 控制台中) . console.cloud.google.com/apis/credentials
    • 啊哈,感谢您提供更多信息!