Why the reproduced request gets a 403
App Check adds an attestation token to requests so a backend can tell whether the call came from a recognised app on a recognised device. The app requests the token from Firebase, receives a short-lived credential, and attaches it to outgoing calls. A backend that enforces App Check verifies the token before it evaluates the request itself.
A script that copies the captured request has three ways to fail. It may omit the header entirely. It may send the header name the app's HTTP client uses without knowing which header the backend reads. Or it may replay a token that was valid when it was captured and is stale by the time it is sent. Each of those produces the same response, which is why the first task is to find out which one applies.
The failure surface has two layers. Firebase returns 401 when the token is absent, and 403 when the token is present but not accepted. A reproduced request that arrives without the header and gets 401 points at a missing value, while a request that carries a token and still gets 403 points at the token itself or at the App Check registration behind it.
Reading the token to find the mismatch
The token is a signed credential with an audience claim and a project scope. Two values decide acceptance: the Firebase project the token was issued for, and the audience the backend expects. A token minted by an app registered in one project is not accepted by a backend that verifies against another, and a token issued for one audience is refused when the backend expects a different one. Both are configuration facts you can read from the app and confirm against the request that has to pass.
Time is the second check. App Check tokens are short-lived by design, and a backend rejects one that is old, that has already been consumed where single use is enforced, or that was issued under a registration the project has since changed. A capture made yesterday therefore proves nothing about today beyond the shape of the request.
Telling App Check apart from neighbouring failures
App Check is one of several ways a mobile backend rejects requests that did not come from the app. Getting the right one matters because each requires different work.
Signature or timestamp failures produce a 403 as well, but the rejected value is in the request body or in an ordinary header that the app computes from the payload. If changing a body field makes the failure appear and changing it back makes it disappear, the check is bound to the payload rather than to attestation.
Play Integrity occupies the same position in the request but is verified through Google Play rather than Firebase, and its verdicts carry more device signals. Firebase App Check can run on top of Play Integrity as its provider, which is why the two are often found together. In that arrangement the App Check token is the header the backend reads and the integrity verdict is inside it.
A 401 in the same place is usually authentication rather than attestation. It appears when a session token is missing or expired, and it persists when the request is sent with a valid App Check header.
What a reconstruction has to account for
A client that has to call an App Check protected backend cannot avoid the token, because the backend verifies it before anything else happens. The work is to reproduce the issuance path rather than to reuse a captured value.
- The project configuration must be recovered from the app. The Firebase project identifier and the application registration the app uses are values inside the APK, and they decide which token the client can legitimately obtain.
- The audience must match what the backend verifies. Getting this wrong produces a valid token that is refused, which looks like a broken token until the claim is compared.
- Token lifetime must be handled. A client that caches a token indefinitely fails after the validity window closes, so the client requests a new one on a schedule or on the first rejection.
- The provider has to be reproduced as well when it is not the default. Where a backend uses Play Integrity as the App Check provider, the integrity part of the flow is part of the same reconstruction rather than a separate project.
- The rest of the request still has to be correct. App Check replaces a missing or invalid attestation header. It does not repair a signature, a device identifier or a stale session, and those remain separate values to recover.
Working the problem in order
Capture the app's request and list every header the app sends, then remove the ones a standard HTTP client produces on its own. What remains is the app's contribution, and among those is the attestation header. Confirm the header name and value format, check the audience and project, and only then reproduce the request. Test the reproduced call with the header removed as well, because a backend that is only monitoring App Check rather than enforcing it will still accept the request, and that changes the scope of the job.
The project is integrity token reverse engineering when the provider factors in, and app to API work when the whole call has to be rebuilt. Projects start at $120 and most are delivered in 24 to 72 hours.
Related work
Reviewed 28 September 2026 · SReverse research desk