Hardware and Attestation

Security documentation

Hardware backed security and attestation

Attestation is a way for a server to ask for evidence about the app, the device, or a cryptographic key before trusting a request. The important part is that the decision is made by a trusted server, not by the same client that might be modified.

TEE

A Trusted Execution Environment AKA "TEE", is an isolated execution area protected from normal Android. It can keep cryptographic keys and security services away from ordinary app processes.

StrongBox

StrongBox is a stronger Android hardware security option backed by a dedicated secure element or integrated secure enclave when the device supports it.

Android key attestation

Android can return an X.509 certificate chain describing a generated key and security properties reported by KeyMint.

Meta Quest attestation

Meta can issue a signed integrity token that your server verifies with Meta to evaluate the Quest app and device state.

Simple rule

Client code collects evidence. The server verifies evidence. The server decides what access the client gets.

Trust model

A useful security design starts by deciding what you trust. Normal application code should usually be treated as untrusted because it runs on a device controlled by the user. A modified app can patch checks, fake return values, remove code, change network requests, or call your backend directly.

Hardware backed security moves some operations into an isolated security boundary. Remote attestation goes one step further by giving your server signed evidence that it can verify independently.

Client

Requests a challenge, asks the platform for evidence, and sends the result to your server.

Platform security

Protects keys or generates signed integrity information using hardware or a trusted platform service.

Server

Checks signatures, freshness, identity, expected app values, replay state, and policy before granting access.

What should never be trusted by itself

A package name sent as normal JSON is only a string. A device model sent by the client is only a string. A SHA256 value calculated by the client is only a string. A boolean such as rooted equals false is only a client claim. Those values become useful only when they are tied to evidence your server can authenticate.

Security layers

TEE, StrongBox, Android key attestation, Meta attestation, user verification, entitlement, and your backend all solve different problems. The easiest way to understand them is to ask what each layer can actually prove.

Layer Main job What it does not prove by itself
TEE Protects sensitive code and cryptographic key operations away from normal Android. It does not prove your APK came from the Meta Horizon Store.
StrongBox Protects supported keys inside more isolated dedicated secure hardware. It does not automatically prove the rest of the app is unmodified.
Android key attestation Lets a remote server verify properties of a generated key and selected device security state. It is not a full replacement for Meta application integrity on Quest.
Meta application attestation Lets your backend evaluate app recognition, signing information, device integrity, freshness, and other Meta supplied claims. It does not prove the player is allowed to perform every backend action.
Meta user verification Helps your backend confirm the Meta user proof for the user identifier being presented. It does not prove device integrity or application integrity.
Entitlement Answers whether the account has access to the application. It does not prove the APK or device is clean.
Your backend session Turns trusted verification results into access to your own services. It is only as strong as the checks used when the session was created.
Easy way to think about it

Hardware protects keys. Attestation reports evidence. Meta verifies Meta platform signals. Your server decides what the player is allowed to do.

Trusted Execution Environment

A Trusted Execution Environment is an isolated environment that runs alongside the normal operating system. Android documents Trusty as one TEE implementation. Trusty runs on the same main processor as Android but is isolated through hardware and software. Android supports other TEE implementations too.

The normal Android side is often called the rich execution environment or normal world. The protected TEE side is often called the secure world. The exact implementation depends on the device platform.

Why it matters

If a private key is created and kept inside secure hardware, normal app code does not receive the raw private key bytes. The app asks Android Keystore to perform an operation such as signing. This makes key extraction harder even if normal Android storage or an app process is compromised.

TEE does not mean invincible

A TEE reduces the amount of software that must be trusted, but it is still part of a real hardware and software system. Vulnerabilities, implementation bugs, outdated firmware, or compromised trusted components can still matter. That is one reason remote attestation includes version, boot, and security level information rather than treating the word TEE as enough.

KeyMint and the TEE

KeyMint is the Android security component that implements key creation and protected cryptographic operations. On a TEE backed device, sensitive KeyMint work runs inside the trusted environment. Android Keystore is the application facing system that routes key requests to the correct KeyMint implementation.

Hardware enforced fields matter more

Android attestation separates some rules into softwareEnforced and hardwareEnforced. A rule in hardwareEnforced is backed by the secure hardware boundary. A rule in softwareEnforced depends more heavily on normal Android remaining trustworthy.

Tip

When your goal is remote trust, do not stop at checking whether a local API says a key is hardware backed. Verify attestation remotely.

How a TEE works

A TEE is not a second normal Android installation. It is a smaller protected environment that receives only specific security work. Android can ask it to create a key, use a key, confirm security state, or enforce restrictions without exposing the private key to ordinary application memory.

Normal world and secure world

Normal world

Your game, Android framework code, most services, and the Linux kernel run here. This side has much more code and therefore a much larger attack surface.

Secure world

The TEE runs a smaller trusted operating environment and selected trusted services. Access is controlled through narrow interfaces instead of normal process memory.

Hardware boundary

The processor and platform security features keep the secure environment isolated from normal software. The exact implementation depends on the device.

What happens when a protected key signs data

1

Your app asks Android Keystore to use a key.

2

Android checks whether the request matches the key rules.

3

The cryptographic operation is passed to KeyMint in secure hardware when the key is hardware protected.

4

The secure environment performs the operation without returning the private key material.

5

Your app receives only the result such as a signature.

Why this helps

If normal Android is compromised, an attacker may still try to ask the secure hardware to use a key. The important difference is that extracting the private key itself is much harder. Key restrictions can also limit what operations the secure hardware allows.

What TEE attestation means

When Android attestation reports TrustedEnvironment for the relevant security level, the attestation is saying the protected operation is backed by a trusted execution environment rather than ordinary software. Your server should still validate the certificate chain, challenge, boot state, application identity, and every other field required by your policy.

TEE is not the same as root detection

A local root detector looks for signs inside Android. TEE backed attestation gives your server evidence from a more trusted boundary. Local root checks can still add telemetry, but they should not replace remote attestation.

StrongBox

StrongBox is Android terminology for KeyMint running in dedicated secure hardware such as an embedded secure element or integrated secure enclave. Android describes StrongBox as providing stronger isolation and tamper resistance than a normal TEE implementation.

StrongBox hardware has its own processing and secure storage capabilities. Android also requires security properties such as a true random number generator and secure timing support. Because it is more isolated and resource constrained, it can be slower and support fewer algorithms or concurrent operations.

TEE compared with StrongBox

Area TEE StrongBox
Isolation Protected execution environment on the main processor Dedicated secure hardware or secure enclave
Android attestation level TrustedEnvironment StrongBox
Performance Usually faster and more flexible Usually slower and more constrained
Use case Strong default for hardware backed keys Higher assurance when the hardware supports it and the threat model needs it

Do not assume every Android device or every Quest model exposes StrongBox to your application. Detect support and design a policy that has a clear fallback instead of silently pretending StrongBox was used.

Android exposes StrongBox beginning with Android 9 APIs. Apps can request StrongBox backing when generating supported keys, but the request can fail if the secure hardware is unavailable or does not support the requested key parameters.

StrongBox details

StrongBox is intended for cases where TEE isolation is not enough for the threat model. Android requires StrongBox implementations to use dedicated secure hardware with its own processing, protected storage, secure timing, and strong random number generation.

Why StrongBox is harder to attack

Separate processing

StrongBox uses dedicated secure processing instead of relying only on the main application processor.

Protected storage

Key material and secure state are kept in storage designed to preserve confidentiality, integrity, authenticity, and freshness.

True random number generation

StrongBox hardware includes its own source of unpredictable random values for cryptographic work.

Secure timing

A secure timer helps enforce rules without trusting the normal Android clock.

Physical attack resistance

The Android compatibility requirements include resistance to physical tampering, glitch attacks, and side channel leakage.

StrongBox support is not guaranteed

Android exposes FEATURE_STRONGBOX_KEYSTORE so an app can check whether the device advertises StrongBox support. A key request can still fail if the requested algorithm or key size is not supported by the StrongBox implementation.

What to do if StrongBox is unavailable

Decide your fallback before shipping. A normal consumer login might accept a TEE backed key. A very sensitive operation might refuse to continue without StrongBox. The important part is to make the decision explicit instead of silently claiming StrongBox protection when the key actually fell back to something else.

StrongBox is slower for a reason

StrongBox has fewer resources and supports fewer concurrent operations than the main processor. It is best used for security critical key operations, not for every high frequency game operation.

Common misunderstanding

StrongBox protects supported cryptographic keys and operations. It does not turn a modified APK into a trusted APK and it does not replace server validation.

Android Keystore

Android Keystore lets an application create and use cryptographic keys without handling the raw secret key material directly. Depending on the device, keys can be software backed, TEE backed, or StrongBox backed.

Security level

For newer Android targets, KeyInfo.getSecurityLevel can report whether a key is in software, a trusted environment, or StrongBox. This local value can help your app understand where a key lives, but a remote server should use attestation when it needs to trust that answer.

Key restrictions

Keys can be created with restrictions such as allowed algorithms, allowed purposes, digests, padding modes, and user authentication requirements. Hardware backed implementations can enforce some of these restrictions outside normal Android.

Important distinction

Hardware backed key storage protects a cryptographic key. It does not automatically prove that every line of your APK is untampered. Android key attestation and Meta application attestation answer related but different questions.

A hardware key is usually nonexportable

When secure hardware protection is active, the private key material is not handed to your application as raw bytes. The app receives a key handle and asks Keystore to perform allowed operations.

Key rules cannot simply be changed later

Android binds key authorizations when the key is created or imported. If you need different key purposes, algorithms, authentication rules, or security requirements, create a new key instead of assuming the old key can be upgraded in place.

Key lifecycle

A secure key design is more than generating a key once. You need to know where it was created, what it is allowed to do, how the server learns its public key, how long it stays trusted, and what happens when the app is reinstalled or the security state changes.

Creation

Create the key through Android Keystore with the exact purpose you need. If the key only signs challenges, do not give it unrelated permissions. If you need StrongBox, request it and confirm through attestation that the resulting security level matches the policy.

Attestation

Use a fresh server challenge when the key is first registered. Send the certificate chain to the backend and validate it before associating the public key with an account or device record.

Registration

After the chain passes, store the public key or a stable fingerprint of it on the server together with the account, creation time, security level, and the policy result. Do not store the private key because the whole point is that it remains on the device.

Proof of possession

Later, your backend can send a fresh challenge and require the device to sign it with the registered private key. The server verifies the signature with the stored public key. This proves the requester currently has access to that private key without exposing it.

Rotation

Plan how to replace a key after reinstall, reset, corruption, or a policy change. A new key should go through attestation again instead of inheriting trust from an old key automatically.

Revocation

Your backend should be able to disable a registered key if an account is recovered, suspicious activity is confirmed, or you intentionally invalidate old registrations.

Useful pattern

Attest once to register a hardware protected key, then use fresh signed server challenges for normal proof of possession. Reattest when your policy says the original device evidence is too old.

Android key attestation

Android key attestation lets a remote verifier examine an X.509 certificate chain associated with a key created in Android Keystore. The attestation extension describes the key and selected device security properties at the time the key was created.

Basic flow

1

Your server creates a fresh challenge.

2

The app generates an Android Keystore key with that challenge as the attestation challenge.

3

The app retrieves the certificate chain for the key.

4

The app sends the certificate chain to your server.

5

Your server verifies the chain, root, revocation status, attestation challenge, security level, application identity, boot state, and the fields required by your policy.

Why the challenge matters

The challenge binds the attestation to a request your server created. If you accept an old certificate chain with an unrelated or reused challenge, an attacker may replay evidence that was valid earlier.

Server side verification is required

Android explicitly recommends sending the attestation certificate chain to a separate trusted server. Verifying the chain only on the same potentially compromised device defeats the point of remote verification.

What the attestation is really binding together

A successful result can bind a public key, a server challenge, security level, key authorizations, application identity information, and boot state into signed evidence. Your server should only trust the pieces that are actually present and validated in the attestation record.

Android attestation verification

The safest way to verify Android key attestation is to treat the certificate chain and the attestation record as separate jobs. First prove the chain is trusted. Then interpret the attestation fields. A parser that can read the fields is not automatically a secure verifier.

Recommended verification order

1

Receive the complete X.509 certificate chain from the device.

2

Build and validate the certificate path to a currently trusted Android attestation root.

3

Verify every certificate signature in the path.

4

Check every applicable certificate against the official Android attestation revocation status list.

5

Locate the first trusted key attestation extension according to Android guidance.

6

Decode the attestation record and compare the challenge with the server challenge.

7

Check attestation security level and KeyMint security level.

8

Check root of trust, lock state, Verified Boot state, application identity, patch information, and key authorizations required by your policy.

9

Consume the challenge so the same attestation cannot be accepted again for another registration.

Do not assume the leaf certificate contains the trusted extension

Android warns that an attacker can extend a certificate chain and add an untrusted attestation extension. Your verifier should follow the documented chain rules and trust only the first valid occurrence in the trusted part of the chain.

Revocation matters

Google maintains an official attestation certificate status list. Certificates can be revoked or suspended after key leakage, CA problems, software flaws, or other security events. Cache the list according to its HTTP cache instructions instead of downloading it for every request.

Root changes matter

Do not pin one old root forever. Android currently documents multiple valid attestation roots, including the newer root that started signing chains on February 1, 2026.

Remote Key Provisioning

Modern Android also uses Remote Key Provisioning for attestation keys. Android states that devices launching with Android 16 support only Remote Key Provisioning for this purpose. This changes how attestation keys are provisioned and allows more targeted revocation than older factory provisioned keys.

Use a tested verifier

Android currently recommends its attestation verification Kotlin library because certificate path and attestation edge cases are easy to miss in custom validation code. If you write your own verifier, test it against malformed chains and unusual valid chains instead of testing only the happy path.

Certificate chain

The attestation result is a certificate chain. The attested key certificate appears near the start of the chain and a trusted root appears at the end. Each certificate must be validated as part of the chain.

Checks that matter

Chain signatures

Verify that each certificate is correctly signed by the next trusted certificate in the chain.

Trusted root

Use the official Android attestation roots required by your policy. Google added a new attestation root that began signing chains on February 1, 2026, so do not hardcode only an older root.

Revocation

Check Android attestation certificate revocation information. A valid signature is not enough if a key has been revoked.

Correct extension

The Android key attestation extension uses object identifier 1.3.6.1.4.1.11129.2.1.17.

First trusted occurrence

Android warns not to blindly assume the attestation extension is always in the leaf certificate. Find the first occurrence according to the documented chain rules and do not trust attacker added extensions later in a manipulated chain.

Important

A certificate that parses successfully is not the same as a certificate that is trusted. Parsing, chain validation, root trust, revocation checks, and policy checks are separate steps.

Important Android attestation fields

The exact schema depends on the attestation and KeyMint version. These are some of the fields most useful for understanding device and key state.

Field What it tells you
attestationSecurityLevel Where the attestation operation is protected. Values include Software, TrustedEnvironment, and StrongBox.
keyMintSecurityLevel The security level of the KeyMint implementation protecting key operations.
attestationChallenge The challenge supplied when the key was attested. Your server should compare it to the challenge it issued.
rootOfTrust Contains Verified Boot information including boot key, lock state, boot state, and on newer versions the verified boot hash.
osVersion The Android OS version value reported through KeyMint.
osPatchLevel The system security patch level.
vendorPatchLevel The vendor image security patch level on supported attestation versions.
bootPatchLevel The boot or kernel image security patch level on supported attestation versions.
attestationApplicationId The Android platform view of the applications allowed to use the attested key. It can contain package and signing certificate information.
softwareEnforced Authorization values enforced by the Android platform rather than directly by secure hardware.
hardwareEnforced Authorization values enforced by the TEE or StrongBox implementation.
purpose The operations the attested key is authorized to perform, such as signing or decryption.
origin Describes how the key entered KeyMint, such as secure generation or import.

Hardware enforced and software enforced

The attestation record separates authorization information into softwareEnforced and hardwareEnforced lists. A field appearing in hardwareEnforced has a stronger meaning than the same idea being enforced only by normal Android software.

Verified Boot

Android Verified Boot creates a chain of trust from a hardware protected root through the bootloader and verified partitions. Attestation can expose information about that boot state through rootOfTrust.

State Meaning
Verified A trusted chain extends from the hardware root of trust through the verified boot process.
SelfSigned Boot verification used a user configured root instead of the manufacturer root.
Unverified The bootloader is unlocked and the normal verified chain of trust cannot be established.
Failed Boot verification failed and the rest of the root of trust information should not be treated as trustworthy.

deviceLocked

A locked bootloader is an important part of the trust decision because an unlocked bootloader allows system software to be replaced. Do not use boot state alone as your complete security policy. Combine it with the certificate chain, security level, challenge, app identity, patch state, and your own server session rules.

verifiedBootKey

This value is a digest connected to the public key used for the Verified Boot chain of trust. Its meaning changes with the boot state, so interpret it together with deviceLocked and verifiedBootState.

verifiedBootHash

On attestation versions that include it, this represents a digest of data protected by Verified Boot. Android documents the VBMeta digest here for devices using the Android Verified Boot reference implementation.

Nonces and replay protection

A nonce is a value intended to be used once. In an attestation flow, your server creates a fresh unpredictable value and later requires the returned attestation evidence to contain that exact value.

Good nonce behavior

Generate it on the server

Use a cryptographically secure random generator. Do not let the client choose the only value used for replay protection.

Store it with context

Associate it with the expected user, session, request type, creation time, and expiry where appropriate.

Compare exactly

After verification, require the attested nonce to match the server record exactly.

Consume it once

Mark it used in an atomic server operation before granting the protected action.

Expire it quickly

A challenge for a login or sensitive operation normally should not remain usable for a long period just because the signed token itself has a longer maximum validity window.

Binding a nonce to a request

For a sensitive action, a strong design can bind the challenge to information such as a server session identifier, transaction identifier, or a hash of important request data. The server recomputes the expected value when the protected request arrives and checks that the verified attestation carries the same nonce.

Meta Quest Attestation API

Meta Quest Application Attestation is a platform service for evaluating whether a request comes from a genuine app binary and a genuine Meta Quest device. Meta documents support for Quest 2, Quest Pro, Quest 3, and Quest 3S. The API is for Android platform apps and requires the client to communicate with Meta attestation services.

Meta flow

1

Your application server creates a unique challenge_nonce.

2

The server sends the challenge to the Quest app.

3

The app calls DeviceApplicationIntegrity.GetIntegrityToken with the challenge.

4

Meta collects integrity signals and returns a signed attestation token to the app.

5

The app sends that token to your server.

6

Your server sends the token to Meta token verification.

7

Meta verifies the token and returns claims to your server.

8

Your server validates the claims and applies your access policy.

Current Meta requirements and limits

Challenge format Non wrapping Base64URL string created from random bytes by the application server
Challenge length 22 to 172 characters
SDK requirement in Meta Unity documentation At least v55 of the Meta XR SDK and Meta Horizon OS for the documented integration
Default token lifetime 24 hours
Device request rate limit 100 requests per hour and 200 requests per day per device
Tip

Token lifetime and challenge lifetime are different things. Even if a token can be valid for hours, a one use login challenge can still be accepted only once and expire after a short server defined window.

Meta attestation details

Meta application attestation is most useful when you treat the returned token as evidence that still needs a policy decision. Meta verifies the token signature for you and returns claims. Your backend then decides which claim values are acceptable for the action being requested.

What Meta is trying to answer

Is the application recognized

The app integrity state tells you whether Meta recognizes the installation as coming from the Horizon Store.

Is the signing identity what you expect

The package certificate digest lets your backend compare the signed claim with the certificate digest values that belong to your real release.

Is the package name what you expect

The package ID claim should match the package ID configured on your server.

Is the Quest device trusted

The device integrity state reports Advanced, Basic, or NotTrusted based on Meta security signals.

Is the response fresh

The nonce, timestamp, and expiration give your server enough information to reject replayed or stale evidence.

Advanced

Meta describes Advanced as a device with a locked bootloader, a valid Android system image, and no signs of system compromise. This is the strongest normal device integrity result documented for the API.

Basic

Basic still means the bootloader is locked and the Android image is valid, but another integrity check failed. Meta specifically notes that this may indicate compromise after boot, including rooting. Whether Basic is acceptable depends on your threat model.

NotTrusted

NotTrusted means Meta saw an unlocked bootloader or an invalid Android system image. For an endpoint that is supposed to require trusted Quest hardware, this should normally fail the integrity policy.

StoreRecognized

StoreRecognized means Meta recognizes the app as legitimately installed through the Meta Quest Store. A server protecting a public store build can normally require this value together with the expected package ID and signing digest.

NotRecognized and NotEvaluated

NotRecognized means the installation is not recognized as legitimate. NotEvaluated means Meta could not identify the app for that evaluation. Do not silently convert either value into StoreRecognized. Decide whether development builds need a separate test policy.

Timestamp should be recent

Meta recommends checking that the timestamp is recent, within a few minutes or less for a normal validation flow. This is separate from the maximum token expiration time. A token that has not expired can still be too old for a login you want to treat as fresh.

Token caching

Meta allows applications to cache attestation tokens within their validity period and recommends caching to avoid rate limits. Your backend should still decide whether a cached token is fresh enough for the current action. Login and account recovery can use a stricter freshness rule than a low risk request.

Rate limits

Meta currently documents a limit of 100 attestation requests per hour and 200 per day for each device. Do not request a new token for every network packet or every gameplay event.

Error handling

A token request can fail because of network problems or platform issues, but malicious software can also block the call. Meta recommends retries with increasing delay. If the app still cannot obtain a token after the retry policy, protected backend access should not be granted as though attestation passed.

Important

Meta verification success proves the token is authentic. Your own backend still has to check the claims. Signature verification alone is not the final security decision.

Meta token claims

After Meta verifies an attestation token, your server receives attestation claims. Your policy should validate the fields you depend on instead of treating a successful verification response as the entire authorization decision.

Request details

timestamp UTC creation time of the token in seconds.
exp Expiration time. Meta documents a default of 24 hours after creation.
nonce The challenge_nonce supplied to GetIntegrityToken. It must match the server challenge you expected.

App state

app_integrity_state StoreRecognized means the app installation is recognized as legitimate by the Meta Quest Store. NotRecognized indicates an illegitimate installation. NotEvaluated means the application could not be identified for that evaluation.
package_cert_sha256_digest SHA256 digest values for the app signing certificate signatures. Your server can compare them with the signing certificate values you expect.
package_id The package name reported in the attestation claims.
version The package version reported in the attestation claims.

Device state

Advanced Meta reports a locked bootloader, valid Android system image, and no signs of system compromise.
Basic Meta reports a locked bootloader and valid Android system image, but one or more other integrity checks failed and may indicate compromise after boot.
NotTrusted Meta reports an unlocked bootloader or an invalid Android OS image.
unique_id App scoped device identifier used by the attestation service. Meta states that it changes every 30 days for privacy.
security_update_pending_days Number of days since the oldest public security patch missing on the device. Meta advises caution because updates may reach users at different times.

How to read the claims safely

Do not compare the entire decoded object with one fixed JSON shape. Read the fields your policy requires, validate their types and allowed values, and safely ignore new fields you do not use. This keeps a future Meta claim addition from breaking your authentication flow.

Package certificate digests

The certificate digest field can contain more than one value. Keep a server side set of signing certificate digests you currently trust and compare the verified claim against that set. This also gives you a controlled way to handle legitimate signing changes.

Version policy

The version claim can help enforce a minimum supported app version, but do not use it as the only integrity check. A valid version string does not replace StoreRecognized, signing digest validation, or device integrity.

Important

Write your claim parser to tolerate new unknown fields. Reject bad values in fields you require, but do not crash just because Meta adds another claim later.

Meta device bans

Meta provides a device ban feature that works with the Attestation API. Your backend identifies the device through the app scoped unique ID from a verified attestation result and sends the ban request to Meta from the server.

How the ban signal is used

1

Your backend verifies a normal Meta attestation token.

2

The backend reads the current unique ID from the verified device state.

3

Your backend creates a device ban through the Meta server interface when your moderation policy allows it.

4

Later attestation results can include a device ban section showing that the device is banned.

5

Your own backend enforces the restriction when it sees the ban status.

The unique ID rotates

Meta states that the attestation unique ID is scoped to your application and changes every 30 days for privacy. Do not build a permanent hardware identity system around the assumption that this value never changes.

Keep the ban ID when you create a ban

Meta returns a ban ID for a created ban. Meta documents that the ban ID can continue to identify that ban even after the current unique ID has rotated, until the ban expires or is reversed.

Access requirement

The device ban feature requires Attestation API integration and approval through Meta Data Use Checkup. The normal attestation API itself and the optional device ban feature should be treated as separate parts of your design.

Use bans carefully

Attestation can help identify severe repeated abuse, but a ban decision is still your responsibility. Keep moderation evidence and account controls separate from the raw integrity verdict so one temporary device problem does not automatically become a permanent punishment.

Meta user verification

Meta User Verification is separate from application attestation. User Verification helps your server verify that a Meta account identifier supplied by the client is valid for the user providing the proof.

The client obtains a user proof nonce through the Platform SDK. Your trusted server sends that nonce and the Meta user identifier to Meta using the user_nonce_validate server to server endpoint. A valid response confirms the user proof. Meta also documents retrieving a verified org scoped ID after the user proof is validated.

What it does not replace

Meta states that User Verification does not replace the Entitlement Check. User identity, ownership entitlement, app integrity, and device integrity are different signals and should not be treated as interchangeable.

Bind the verified user to your own session

After the backend verifies the Meta user proof, connect that verified identity to the same server login attempt that passed your application and device integrity checks. Do not verify the user in one request and then trust a different client supplied user ID later.

Useful separation

Question Relevant mechanism
Is this the expected Meta user User Verification
Does the user own or have access to the app Entitlement Check
Is this recognized app binary and device state acceptable Meta Quest Attestation API
Is this Android Keystore key hardware backed with expected properties Android key attestation

Combining security signals

The strongest design does not ask one check to do every job. Use each signal for the question it is good at, then let the backend combine the results into one session decision.

Example Quest login

1

The backend starts a login attempt and creates a fresh one use nonce.

2

The app obtains Meta application attestation using that nonce.

3

The backend verifies the Meta token and checks freshness, app identity, signing digest, and device integrity.

4

The backend verifies the Meta user proof for the account identifier.

5

The backend performs the entitlement check if ownership is required.

6

If you use an Android hardware key, the backend can also verify its registration or require proof of possession.

7

The backend binds the verified account and approved security state to its own short lived session.

Why this is better than one giant check

If account proof fails, you know the identity layer failed. If the package digest is wrong, you know the app identity layer failed. If device integrity is NotTrusted, you know the platform integrity layer failed. Keeping the checks separate makes logs easier to understand and policy changes safer.

Use hardware keys for continuity, not as a replacement for attestation

A registered TEE or StrongBox key can be useful for proving that a later request comes from the same protected key. It does not automatically tell you that the current app still has the same Meta integrity state as when the key was registered. Reattestation is still useful when you need a fresh platform verdict.

Use server sessions to avoid checking everything constantly

Once the login passes, create your own session with a clear expiration time and the security facts you care about. Normal API requests can validate that session. Sensitive actions can require a fresh integrity check when needed.

Store the result, not every secret

Store safe facts such as verification time, account identifier, allowed integrity level, key fingerprint, package version, and policy result. Avoid storing raw reusable tokens longer than necessary.

Server validation

The server should be the security boundary. It should not accept a client result such as attestationValid equals true. It should validate the real evidence or ask the platform provider to validate it.

Recommended order for Meta application attestation

1

Look up the server side challenge record.

2

Reject missing, expired, already used, or wrong session challenges.

3

Send the attestation token to Meta verification from the server.

4

Require Meta verification success.

5

Decode the returned claims.

6

Compare nonce with the server challenge.

7

Check timestamp and expiration against your own freshness policy.

8

Check package_id, signing certificate digest, app integrity state, and acceptable device integrity state.

9

Atomically consume the challenge.

10

Issue your own short lived authenticated server session.

Keep secrets off the client

Meta server to server credentials belong only on your backend. Do not put an app secret or server access token in Unity code, JavaScript shipped to a browser, an APK asset, or a public repository.

Fail closed for protected actions

For high value actions, a missing attestation result should not silently become success. Meta specifically warns that compromised clients may block or strip the attestation call. Use controlled retries for temporary network problems, then apply the same cautious policy you would apply to an untrusted integrity result.

Example security policy

A policy should match your app instead of copying the strictest possible setting. A multiplayer game login, a purchase, an account recovery action, and a low risk public endpoint do not need identical rules.

Example login policy

Require

Valid server challenge, successful Meta token verification, exact nonce match, expected package ID, expected signing certificate digest, recognized app state, acceptable device integrity state, recent token timestamp, and a challenge that has not been used before.

Then issue

A server session token with a shorter lifetime than the attestation token. Bind the session to the verified account or internal player identity.

Recheck

Run attestation again when the session expires, after a meaningful risk event, or before a sensitive operation that needs fresher integrity evidence.

Patch policy

Do not automatically ban every device that is one day behind a new security update. Meta warns that update rollout speed varies. A reasonable policy can log patch age, apply a warning window, and become stricter only when the risk of a known old build justifies it.

Basic versus Advanced

If your threat model needs the strongest Meta device signal, require Advanced. If your app can safely support Basic, document exactly what access Basic receives. Do not accidentally treat NotTrusted as a temporary warning if the protected endpoint is supposed to require an intact device.

What attestation cannot prove

Attestation is strong evidence, not magic. A good design understands its limits.

It does not make client code secret

Attackers can still inspect application files, native libraries, network behavior, and public protocol formats.

It does not replace authorization

A genuine app on a genuine device does not automatically mean the user is allowed to perform every action.

It does not validate your game rules

Server authoritative gameplay, rate limits, inventory validation, purchase validation, and abuse prevention logic still matter.

It does not make old evidence fresh

A signed token can still be inappropriate for a new transaction if the challenge was not bound to that transaction or if your freshness window is too large.

It does not prove a person is honest

Integrity signals describe software, hardware, or account proof. They do not replace moderation, fraud detection, or behavioral controls.

Common mistakes

Validating only on the client

A modified client can patch the result. The server needs to make the final decision.

Trusting client supplied hashes

A client can send the hash value you expect. Prefer signed attestation claims or server side platform verification.

Reusing nonces

A reusable challenge turns fresh attestation into replayable evidence.

Checking only the signature

A valid signed token still needs claim checks. Validate nonce, time, app identity, signing certificate, and integrity verdicts.

Hardcoding one Android attestation root forever

Trust roots can rotate. Android added another root for chains beginning February 1, 2026. Follow the official root list rather than copying an old single certificate from a tutorial.

Confusing identity with integrity

Knowing the Meta account does not prove the APK is intact. Knowing the APK is recognized does not prove the current account owns the app.

Using a permanent device identifier assumption

Meta documents that the attestation unique_id is app scoped and rotates every 30 days.

Rejecting every temporary attestation error immediately

Network and infrastructure failures happen. Retry with backoff, but do not turn repeated failure into automatic trust.

Testing

Test the negative path as carefully as the success path. A security system that works only when every service is healthy is incomplete.

Test Expected server behavior
Correct token and correct nonce Continue only if all required claims also pass.
Correct token with wrong server nonce Reject.
Same challenge submitted twice First valid use may succeed. Later use is rejected.
Wrong package ID Reject.
Wrong signing certificate digest Reject.
Expired token Reject and request fresh evidence.
Old timestamp inside your freshness window policy Apply the policy you documented. Do not rely only on maximum token lifetime.
Meta service unavailable Retry with backoff. Protected operations remain unavailable if verification cannot be established.
Unexpected new claim Ignore unknown fields safely while continuing to enforce known required fields.

Validation tips

Good attestation is mostly about what your server refuses to trust. Keep the rules small, check the important values every time they matter, and never let the client decide whether it passed.

Server side tips

Create the nonce on your server

Do not let the client choose its own challenge. Generate a random nonce on the backend, save it with a short expiration time, and send that nonce to the client.

Use every nonce once

After a valid attestation uses a nonce, mark it used in the same database operation that accepts the request. Two requests should never be able to spend the same nonce at the same time.

Bind the nonce to the login

Save which user, login attempt, session, or protected action the nonce belongs to. A valid token for one login should not be accepted for another login.

Compare exact app identity

Check the returned package ID and signing certificate digest against values stored on your server. Do not accept the package name or expected hash from the client.

Check the integrity verdict

A token can be correctly signed and still contain a result your app should reject. Check the app integrity state and device integrity state instead of stopping after token verification succeeds.

Use your own freshness window

Meta tokens have their own expiration, but your backend can require a much newer token for login or a sensitive action. Check the timestamp, expiration, and nonce together.

Issue a backend session after success

You usually do not need to run full attestation on every normal game request. After a successful login check, issue your own short lived authenticated session and validate that session on later requests.

Recheck when the risk changes

Run a fresh check for a new login, a new device session, account recovery, important purchases, or other actions where an old integrity result is not enough.

Do not trust these from the client

Client value What the server should do instead
attestationValid Verify the real token with Meta and check the returned claims.
package ID Compare the verified claim with the package ID stored in server configuration.
signing hash Compare the verified signing digest with a server side allowlist.
device integrity result Use the value returned by the trusted attestation result.
user ID by itself Verify the user through the platform flow and bind it to the server session.
request timestamp Use server time for expiration and replay decisions.

Android hardware attestation tips

Validate on a separate trusted server

Do not validate a hardware attestation chain only on the same Android device that produced it. A compromised device could also interfere with the local validation code.

Validate the whole certificate chain

Check the trusted root, every certificate signature in the chain, certificate validity, and revocation status. Successfully parsing the certificates is not enough.

Check the challenge

The attestation challenge should match the fresh challenge your server created. This proves the attestation was created for the request your server is currently checking.

Check where the key lives

Read the attestation security level and KeyMint security level. If your policy requires hardware protection, do not silently treat a software backed result as equivalent to TEE or StrongBox.

Check boot state when your policy needs it

Verified Boot information can help show whether the device booted software that matches its trusted boot configuration. Decide which states your app accepts before you ship.

Keep trusted roots current

Do not copy one old Android attestation root into your backend and forget it. Google can rotate or add attestation roots, so keep your trust list based on the current Android documentation.

Small things that prevent big mistakes

Keep secrets on the backend

Meta app secrets, server access tokens, database credentials, and signing secrets should never be shipped inside the APK.

Use generic public errors

The client usually only needs to know that authentication failed. Keep the exact failed check in private server logs so you do not give attackers a map of your validation rules.

Do not log raw secrets

Redact attestation tokens, session tickets, access tokens, app secrets, and other reusable credentials. Log request IDs and safe identity fields instead.

Rate limit challenge creation

Do not allow one client or account to create unlimited challenges or verification requests. Expire unused challenges and limit how many can be active at once.

Separate network failure from failed integrity

A timeout is not the same thing as a bad integrity verdict. You can retry temporary service errors while still refusing protected access until validation actually succeeds.

Test the failure paths

Try an expired nonce, reused nonce, wrong package, wrong signing digest, expired token, missing token, and two requests using the same nonce. Security code is only useful if the rejection paths work too.

Simple rule

Trust verified evidence from the platform, values your own server created, and data already tied to an authenticated server session. Treat everything else from the client as input that still needs validation.

Official references

These are the primary sources used for the technical details in this site. Check them again before shipping security logic because platform behavior, SDK requirements, trust roots, limits, and recommendations can change.

Content reviewed against official documentation on August 20, 2026.