The thing the Firebase CLI forgot to build.
Running your app on a simulator with App Check enforced? Then your backend keeps
rejecting it with 401 / App attestation failed β because the simulator can't do
App Attest and its debug token isn't registered.
The fix lives in the Firebase Console, buried under App Check β Apps β Manage debug
tokens. There is no firebase CLI command for it. So you click. Every. Single.
Time a teammate or CI machine spins up a new simulator.
appcheck-debug-token makes it one line:
$ appcheck-debug-token add -p my-project -b com.example.app
β’ Checking gcloud authβ¦
β gcloud access token acquired
β’ Resolving project 'my-project'β¦
β Project: my-project (number 1234567890)
β App Check API enabled
β Resolved app: 1:1234567890:ios:abc123def456
β Access OK (3 existing token(s))
β’ Relaunching com.example.app to force the token to printβ¦
β Extracted token from logs
β Token format valid: 1B2C3D4E-5F60-7182-9304-0011223344FF
β’ Registering 'my-mac-20260619-0930' on 1:1234567890:ios:abc123def456β¦
β Registered (resource: a1b2c3d4e5f6)
β’ Verifyingβ¦
β Verified β 'my-mac-20260619-0930' is registered. App Check calls now pass.No token to copy-paste. No console. No dashboard. β¨
- π§© Works for any project & app β project id or number, app id auto-resolved.
- π€ Zero copy-paste β pulls the debug token straight from a booted iOS Simulator's logs.
- π§ͺ Three verbs β
list,add,delete. Plus--dry-runfor a no-op rehearsal. - π Safe by design β sanity-checks every step (auth, project, API, permissions, token format) and verifies the write by reading it back.
- π¦ No dependencies β pure Python standard library. One file.
- π¨ Readable output β colored, step-by-step, machine-friendly (token on stdout).
| Tool | Why | Needed when |
|---|---|---|
| Python β₯ 3.8 | runs the script | always |
| gcloud (authenticated) | calls the App Check API | always |
| firebase-tools | resolves the app id | only if you omit --app |
Xcode (xcrun) |
reads the token from a Simulator | only if you omit --token |
You need Firebase App Check Admin on the project. Authenticate once with
gcloud auth login.
Recommended β pipx (isolated, global command):
pipx install git+https://github.com/bogdanmatasaru/appcheck-debug-tokenWith pip:
pip install git+https://github.com/bogdanmatasaru/appcheck-debug-tokenOr just grab the file β it has no dependencies:
curl -O https://raw.githubusercontent.com/bogdanmatasaru/appcheck-debug-token/main/appcheck_debug_token.py
chmod +x appcheck_debug_token.py
./appcheck_debug_token.py --help# 1. See what's already registered (read-only)
appcheck-debug-token list -p my-project
# 2. Add the debug token of a running simulator (auto-detected + relaunched)
appcheck-debug-token add -p my-project -b com.example.app
# 3. Clean up later
appcheck-debug-token delete -p my-project -n my-mac-20260619-0930appcheck-debug-token <command> -p <project> [options]
Commands:
list List the project's App Check debug tokens (read-only).
add Register a debug token.
delete Delete a debug token by display name.
Shared options:
-p, --project GCP/Firebase project id OR number (required)
-a, --app Firebase App ID (1:NUM:ios:HEX) β auto-resolved if omitted
--platform ios | android | web (default: ios)
--dry-run Run all checks but make no changes
add options:
-t, --token Debug token UUID β auto-extracted from a simulator if omitted
-n, --name Display name (default: <host>-<UTC date>)
-b, --bundle Bundle id to relaunch on the simulator to print the token
-u, --udid Simulator UDID (default: the booted one)
delete options:
-n, --name Display name of the token to delete (required)
# Explicit token + friendly name
appcheck-debug-token add -p my-project -t 1B2C3D4E-5F60-7182-9304-0011223344FF -n "Alice's M3"
# Pick a specific simulator + a specific app
appcheck-debug-token add -p 1234567890 -a 1:1234567890:ios:abc123 -u <UDID> -b com.example.app
# Rehearse without writing anything
appcheck-debug-token add -p my-project -b com.example.app --dry-runOn the Simulator, Firebase prints it to the console at launch:
[Firebase/AppCheck] Firebase App Check debug token: '1B2C3D4E-5F60-7182-9304-0011223344FF'.
- Pass
--bundle <id>and the tool relaunches the app and grabs it for you. - Or copy it once and pass
--token. - The token persists per simulator install, so you register it once.
π‘ Physical devices don't need this β they use App Attest, which attests automatically. Debug tokens are a Simulator thing.
gcloud auth print-access-token ββ
βββΊ POST firebaseappcheck.googleapis.com
firebase apps:list (app id) ββββ /v1/projects/<num>/apps/<app>/debugTokens
xcrun simctl log show (token) βββ
It resolves your project number + app id, ensures the App Check API is enabled,
checks you actually have access, validates the token, registers it, then reads it
back to confirm. The required X-Goog-User-Project quota header is sent for you
(the usual cause of a mysterious 403).
| Symptom | Fix |
|---|---|
Not authenticated |
gcloud auth login |
403 Forbidden |
Your account needs Firebase App Check Admin on the project. |
requires a quota project |
Handled automatically β but ensure your project id is correct. |
No debug token in the last 5m of logs |
Launch the app on the simulator (or pass --bundle / --token). |
Multiple ios apps |
Pass --app <id> to disambiguate. |
| App still rejected after adding | Make sure the simulator app actually uses that token (it's printed at launch), and that you registered it under the right app id. |
Issues and PRs welcome! It's a single, dependency-free file β easy to hack on.
MIT Β© Bogdan Matasaru