Skip to content

feat(firebase): tvOS support for firebase_app_check - #18

Open
TheNoumanDev wants to merge 6 commits into
fluttertv:mainfrom
TheNoumanDev:feat/firebase-app-check-tvos
Open

feat(firebase): tvOS support for firebase_app_check#18
TheNoumanDev wants to merge 6 commits into
fluttertv:mainfrom
TheNoumanDev:feat/firebase-app-check-tvos

Conversation

@TheNoumanDev

@TheNoumanDev TheNoumanDev commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds federated firebase_app_check_tvos — Firebase App Check for Apple TV, built on the Firebase Apple SDK. Re-exports the firebase_app_check Dart API and ships the native tvOS pluginClass; depends on firebase_core_tvos.

DeviceCheck, App Attest, and Debug attestation providers work on tvOS 15+. reCAPTCHA is excludedRecaptchaProvider is not part of the Firebase tvOS SDK; its Pigeon "recaptcha" case is kept as an unsupported-platform stub so registration/dispatch stay intact.

Package(s) touched: firebase_app_check_tvos (new)

How was it tested?

Native FirebaseAppCheck 12.15.0 initializes on tvOS. With the Debug provider, activate() issues a debug token and getToken() exchanges it with the App Check backend (exchangeDebugToken), returning a valid App Check token. Platform.operatingSystem == "tvos" / Platform.isIOS == true.

  • Ran the package's example/ app
  • Verified on tvOS simulator (26.2) — Debug-provider getToken round-trip succeeds against a live project
  • Physical Apple TV — not verified. DeviceCheck / App Attest hardware attestation requires an App ID with the DeviceCheck/App Attest capability under a paid Apple Developer account; it can't be exercised on a free personal team. The providers compile and run on tvOS 15+, but genuine on-device attestation is pending that account. The Debug-provider path is fully verified. Would be great, if you can help with testing it on real device.
  • dart analyze is clean for the package

Versioning & changelog

  • version: set to 0.0.1 (new package)
  • Matching ## 0.0.1 entry at the top of CHANGELOG.md
  • No behaviour change (new package)
  • Semver 0.x: initial 0.0.1

Checklist

  • Only firebase_app_check_tvos files are touched
  • No secrets, absolute local paths, or TODO/debug left
  • README.md documents tvOS behaviour (reCAPTCHA excluded; DeviceCheck/App Attest need a paid account for real-device attestation; version-alignment note)
  • Sibling-package constraint noted below

Notes for reviewers

  • reCAPTCHA disabled on tvOS: RecaptchaProvider is iOS-only in the Firebase SDK. The "recaptcha" handler is kept as an unsupported-platform stub (never removed), so Pigeon dispatch doesn't break.
  • Real-device attestation unverified: DeviceCheck/App Attest need a paid Apple Developer account + capability, so only the Debug provider is verifiable without it — and it's verified here on the simulator (token exchange round-trip).
  • Version alignment (all Firebase _tvos leaf packages): the native Pigeon matches the firebase_core 4.11.x / firebase_core_platform_interface 7.1.0 train. Mixing firebase_core_tvos with a different firebase_core whose FirebaseOptions list differs can crash in CoreFirebaseOptions.fromList at launch (Dart options: init).

Relavant issue: #12

Federated firebase_app_check_tvos — App Check for Apple TV, built on the
Firebase Apple SDK. Re-exports the firebase_app_check Dart API and ships the
native tvOS pluginClass; depends on firebase_core_tvos.

DeviceCheck, App Attest, and Debug providers work on tvOS 15+. reCAPTCHA
(RecaptchaProvider) is unavailable in the Firebase tvOS SDK — excluded, with
its Pigeon case kept as an unsupported-platform stub so dispatch stays intact.
@TheNoumanDev
TheNoumanDev force-pushed the feat/firebase-app-check-tvos branch from a56b760 to c3ad863 Compare August 14, 2026 00:03
DenisovAV
DenisovAV previously approved these changes Aug 17, 2026

@DenisovAV DenisovAV left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

Clean port, gate-green on the merged tree, and the question I went in expecting to sink it turned out fine. Two small notes below, neither blocking.

App Check on tvOS is real, not theatre

I set out to establish whether attestation actually works here, because App Check exists to gate backend access — a port where only the debug provider functions would be worse than no port, and the README would be claiming protection the device cannot provide. It is not the case. From the tvOS SDK on my machine (AppleTVOS26.5):

DCDevice:            API_AVAILABLE(ios(11.0), macos(10.15), tvos(11.0), watchos(9.0))
DCAppAttestService:  API_AVAILABLE(macos(11.0), ios(14.0), tvos(15.0), watchos(9.0))

Both providers exist, App Attest from tvOS 15 — which is exactly this podspec's floor. FirebaseAppCheck.podspec at 12.15.0 declares tvos_deployment_target = '15.0' and s.tvos.weak_framework = 'DeviceCheck', matching your podspec's weak-link comment. And when a provider genuinely cannot run, AppCheckCore raises unsupportedAttestationProvider: rather than handing back a token — so the failure is loud, which is what matters.

The one hand-edit that narrows reCAPTCHA to #if os(iOS) is not a shortcut either: FIRRecaptchaProvider.h is declared API_UNAVAILABLE(macos, tvos, watchos, macCatalyst), so the guard is required to compile.

Port faithfulness

Delta against firebase_app_check 0.4.5 as published is four hunks: the core import, os(tvOS) added to the messenger gate and to Messages.g.swift, the reCAPTCHA narrowing, and tvOS 14.0 added to the appAttestWithDeviceCheckFallback availability. Constants.swift identical. Nothing behavioural.


Two notes

1. The error-code mapping is off by one, and it lands worst on tvOS. createFlutterError (FirebaseAppCheckPlugin.swift:219-233) reads:

case 0:  // FIRAppCheckErrorCodeServerUnreachable
case 1:  // FIRAppCheckErrorCodeInvalidConfiguration
case 2:  // FIRAppCheckErrorCodeKeychain
case 3:  // FIRAppCheckErrorCodeUnsupported

FIRAppCheckErrors.h at 12.15.0 declares Unknown = 0, ServerUnreachable = 1, InvalidConfiguration = 2, Keychain = 3, Unsupported = 4. Every case is shifted. The consequence is specific to this platform: Unsupported — "App Attest is not available on this device", the most likely error on older Apple TV hardware — is code 4, falls to default, and reaches Dart as unknown; a keychain failure arrives labelled code-unsupported.

It is verbatim upstream, so I would not fix it here — diverging costs you on every rebase. But it is worth a line in PORTING_REPORT.md, so that whoever does the physical-hardware pass does not read an unknown and go looking in the wrong place.

2. The README's reCAPTCHA sentence promises more than the code guarantees. "Requesting it on tvOS leaves the provider unconfigured and getToken surfaces an explicit error" holds when activate() runs before any App Check instance exists. FlutterAppCheckProviderFactory.createProvider pre-configures a DeviceCheck wrapper, and the tvOS recaptcha branch leaves delegateProvider untouched — so if some Firebase product triggered App Check first, tokens keep flowing via DeviceCheck without the error. Either delegateProvider = nil in that branch, or soften the sentence. Upstream has the same shape on macOS, so this is a faithful port of an upstream wart; only the README makes a claim about it.

Nit: the tvOS 14.0 in the fallback #available is constant-true against a 15.0 floor, so the DeviceCheck fallback is unreachable on tvOS. 15.0 would be more honest.

LGTM once you decide on the two notes — neither needs to hold the merge.

@DenisovAV
DenisovAV dismissed their stale review August 17, 2026 01:04

Dismissing my own approval — it was the wrong state for this review. The body asks for a change (either delegateProvider = nil in the tvOS reCAPTCHA branch, or softening the README sentence that promises an explicit error) and for a note in PORTING_REPORT about the off-by-one error codes. A review that asks for changes should sit as a comment, not an approval; "LGTM once you decide on the two notes" is not something an approval can express. The findings themselves stand as written.

@DenisovAV DenisovAV left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review — both notes closed

I offered two ways to resolve the reCAPTCHA discrepancy, and the one you took is the right one. The README no longer promises an explicit error; it now says that when reCAPTCHA is requested, the tvOS build falls back to the default DeviceCheck provider. That is what the code does, rather than what one would like it to do.

The off-by-one error mapping is recorded in PORTING_REPORT.md, which is what I asked for — no divergence from upstream. Whoever runs the physical-hardware pass will no longer read an unknown and go looking in the wrong place when it actually means App Attest is unavailable.

The nit is fixed too: tvOS 14.0 became 15.0 in both places, so those availability checks are no longer constant-true against a 15.0 floor.

Gate clean, CI 18/18.

Retracting my earlier objection: I approved this while it still carried open requests, which was the wrong state for that review. It is the right state now.

LGTM.

@MAUstaoglu

Copy link
Copy Markdown
Member

PORTING_REPORT.md:60-63 gets this right — 0.4.6 is off-train, 0.4.5 is the one to target. But firebase_app_check: ^0.4.5 in the pubspec is >=0.4.5 <0.5.0, so it admits the 0.4.6 the report rules out, and that's what a consumer resolves today. The example pins exactly, which is why CI never sees it.

0.4.6 pulls platform interface 0.4.2, which moved the pigeon contract in two ways the generated Swift here (built against ≤0.4.1) doesn't cover:

  • new getTokenResult channel, public as FirebaseAppCheck.instance.getTokenResult() — no handler registered, so it comes back MissingPluginException
  • activate gained a 5th arg, recaptchaSiteKey. The handler reads args[0...3], so this one doesn't throw — activation succeeds and the site key is dropped silently. Quieter and worse.

firebase_app_check: ">=0.4.5 <0.4.6" covers both. Everything else here is good — reCAPTCHA triage and the fallback note are exactly right. Happy to approve with that one line changed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants