Meta Quest Attestation API
Updated: Nov 17, 2024
The Attestation API helps ensure that your apps remain secure and uncompromised from potential unsanctioned modifications, security breaches, and severe and repeated abuse.
Operating system limitation The Attestation API is exclusive to apps built on the Android Platform.
The Attestation API is only supported on Meta Quest 2, Meta Quest Pro, Meta Quest 3, and Meta Quest 3S.
The Application Attestation API aims to reduce the friction for developer integration while providing a secure environment for applications. The API provides an attestation token that helps determine whether you’re interacting with the following:
- Genuine app binary: Determines whether you’re interacting with an unmodified app binary recognized by the Meta Horizon Store.
- Genuine Meta device: Determines whether your app is running on an unmodified and untampered Meta Quest headset.
By detecting potentially risky and fraudulent interactions, such as a tampered app, your app’s server can respond with appropriate actions to protect your work and intellectual property from piracy, cheating, attacks, unauthorized access, and external data misuse.
Below is a step-by-step overview of the Attestation API call flow:
Note: It’s crucial to emphasize that the Attestation API at the client side must communicate with the attestation server during this process. That’s why a Wi-Fi connection is essential to ensure the success of the Attestation API call. The application client should validate Wi-Fi connectivity before initiating the API call.
Understanding the sequence of steps during an API call flow is important for proper integration of the Application Attestation API. Below is the detailed breakdown:
Nonce Generation - The Application Server generates a challenge_nonce
and sends it to the application client. This challenge nonce is a crucial component of the process. It’s generated server-side, and it’s important that the same challenge nonce is not reused to prevent replay attacks.
Call Attestation API - The Application Client uses the central interface of the Attestation API, ovr_DeviceApplicationIntegrity_GetIntegrityToken(const char *challenge_nonce)
, to retrieve an attestation token. The input challenge_nonce
for this function is received from the Application Server.
Security Signals - The Attestation API collects necessary security signals and forwards them to the Meta Attestation Server. The processes illustrated in Steps 3 and 4 are marked with dash-dotted lines. This signifies that these steps occur in the background, without direct involvement from the application client.
Token Generation - Based on the received security signals, the Attestation Server generates an attestation token. This token is cryptographically signed by the Attestation Server, reinforcing the security and reliability of the process.
Token Receipt - The Attestation API receives the attestation token and sends it to the application client.
Service Request - Once the Meta Quest application receives the attestation token, the application needs to send this token back to its own application server for verification. The verification process takes place on the server side rather than on the client side to enhance security. The Application Client includes the attestation token in its service request and sends it to the Application Server. The crucial aspect here is that the Application Server uses this token to determine if the client has been tampered with.
Token Verification Request - Upon receiving the service request, the Application Server sends a token verification request to the Attestation Server.
Token Verification - The Attestation Server validates the token and sends back a success or failure message along with token claims to the Application Server. If the token is invalid, an error message is sent. However, if the token is valid, token claims are sent.
Service Provision - The Application Server, based on the token verification results, decides whether to deny or provide its service to the application client. If the token verification was successful, the server fulfills the service request from the application client.
Integrating the Attestation API with Native applications Integrating the Attestation API into your Native application requires you to download the Platform SDK for your app.
Navigate to
Oculus Platform SDK and download the Platform SDK. Please ensure that you are using at least v55 of both the Platform SDK and the Meta Horizon OS system.
For a comprehensive understanding of general Platform SDK API integration, please refer to this
API Reference page. Following is an example of code snippet for integrating the Attestation API.
#include <iostream>
#include <random>
#include <vector>
#include <algorithm>
#include "OVR_Platform.h"
// Base64 Url encoding
std::string base64UrlEncode(const std::vector<uint8_t>& data) {
static const std::string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
std::string result;
int val = 0, valb = -6;
for (uint8_t c : data) {
val = (val<<8) + c;
valb += 8;
while (valb >= 0) {
result.push_back(chars[(val>>valb)&0x3F]);
valb -= 6;
}
}
if (valb > -6) result.push_back(chars[((val<<8)>>(valb+8))&0x3F]);
while (result.size() % 4) result.push_back('=');
return result;
}
// The "challenge_nonce" parameter must be a non-wrapping Base64URL-encoded
// string, formed from a random byte array by the application server.
std::string getChallengeNonceFromAppServer() {
std::random_device rd;
std::vector<uint8_t> randomNonce(16);
std::generate(randomNonce.begin(), randomNonce.end(), [&]() { return rd(); });
// Convert to Base64 string
// The length of the challenge_nonce must range from 22 to 172 characters.
std::string challenge_nonce = base64UrlEncode(randomNonce);
return challenge_nonce;
}
void SampleCode() {
std::string challenge_nonce = getChallengeNonceFromAppServer();
// Calling the Attestation API
ovr_DeviceApplicationIntegrity_GetIntegrityToken(challenge_nonce.c_str());
// Get message response from message queue
while(true) { // Can set a poll timer here
// Pop messages off of the message queue
ovrMessageHandle message = ovr_PopMessage();
while (message != nullptr) {
// First check for errors
if (ovr_Message_IsError(message)) {
ovrErrorHandle errorHandle = ovr_Message_GetError(message);
const char* errorMessage = ovr_Error_GetMessage(errorHandle);
// handle the error
}
// Get the message type
ovrMessageType messageType = ovr_Message_GetType(message);
// Handle the response
if (messageType == ovrMessage_DeviceApplicationIntegrity_GetIntegrityToken) {
const char* attestation_token = ovr_Message_GetString(message);
// Send the Attestation token to the app server for token verification
} else {
// In this example, we are disregarding all other message types, thus,
// we will simply ignore them.
}
ovr_FreeMessage(message);
return;
}
}
}
This code above provides a practical example of how to use the Attestation API in a Native application. By following these steps and utilizing the sample code, you can effectively incorporate the Attestation API into your Native project.
Note
The `challenge_nonce` parameter must be a non-wrapping Base64URL-encoded string, created from a random byte array by the application server. Its length must range from 22 to 172 characters. Once an attestation token is returned, it must then be sent to the application server for verification.When the application server receives the attestation token from the application client, the next step is token verification. This can be accomplished by sending the attestation token to the Attestation Server through a RESTful API. To do that, follow these steps.
Select your application.
In the left-side navigation, select Development > API.
In the App Credentials section, copy your access token. It uses the format OC|App_ID|App_Secret
.
In the following endpoint, replace the {access_token}
parameter with the access token and replace the {attestation_token}
parameter with the attestation token received from the application client, which is to be verified.
https://graph.oculus.com/platform_integrity/verify?token=<attestation_token>&access_token=<access_token>
Upon successful verification of the attestation token, you will receive a success response from the Attestation Server, as shown below:
{
"data": [
{
"message": "success",
"claims": "<base64url_encoded_claim>"
}
]
}
The {base64url_encoded_claim}
is a Base64URL-encoded string that represents the claims of the verified attestation token.
In the event that the attestation token’s signature is invalid, the Attestation Server will return a response as shown below:
{
"data": [
{
"message": "invalid signature"
}
]
}
This response indicates that the attestation token could not be verified due to an invalid signature. The application server should treat the client as potentially compromised and may choose to deny the service request from the application client. It’s important for the application server to properly handle these scenarios to maintain the security of the application.
By default, an attestation token remains valid for a period of 24 hours. During the token validation process, if the token has expired, the Attestation Server will return the following error message:
{
"data": [
{
"message": "token expired"
}
]
}
To minimize performance overhead and frequent API calls, application clients can opt to cache the attestation token for a certain duration. This duration should, however, be less than the token’s validity period to prevent usage of expired tokens. This caching strategy provides an optimal balance between ensuring secure communication and maintaining efficient application performance.
Once an attestation token has been successfully verified by the Meta Attestation server, the attestation server sends a Base64URL encoded token to the application server. By Base64URL decoding the token, the application server obtains a set of claims. An example of these claims is as follows:
{
"request_details": {
"exp": 1684606153,
"nonce": "JMxMPp1H6kCxGzPRsjKFLw==",
"timestamp": 1684519753
},
"app_state": {
"app_integrity_state": "NotEvaluated",
"package_cert_sha256_digest": [
"c8a2e9bccf597c2fb6dc66bee293fc13f2fc47ec77bc6b2b0d52c11f51192ab8"
],
"package_id": "com.example.name123",
"version": "1"
}
"device_state": {
"device_integrity_state": "NotTrusted",
"unique_id": "cd81cd7e7e0525f99442ab6392117e89759e8872852de0495aa58f22bd8a1253",
}
}
The claims include three sections: “request_details”, “app_state” and “device_state”.
The request_details section includes information related to the request. Here are the details of each field:
- timestamp: This is the UTC time, in seconds, when the token was created.
- exp: This is the token expiration UTC time, which is set by default to 24 hours after the token is created.
- nonce: This is the same string as the
challenge_nonce
provided by the caller when calling the ovr_DeviceApplicationIntegrity_GetIntegrityToken(const char *challenge_nonce)
API.
Important
Upon receiving a token, the application server must validate the nonce within the request details to prevent potential replay attacks.A Replay Attack
is a security breach where a valid token is intercepted and reused by an attacker in subsequent requests. This often happens when a token is compromised. To counteract this, systems use nonce validation.
In the context of our API, once a token is received, it’s important for the application server to verify that the challenge_nonce
given to the ovr_DeviceApplicationIntegrity_GetIntegrityToken(const char *challenge_nonce)
API matches the nonce
that is returned in the token claim. If these are not identical, the token may have been replayed. In this case, the application server should treat this token as invalid and deny any application-specific service to its client. Additionally, it’s crucial that the application server ensures the nonce is only used once. Reusing the same nonce multiple times can lead to a replay attack. To ensure system security and prevent potential replay attacks, the nonce must be uniquely generated for each use and should not be reused.
app_state
includes the application attestation information. Below are the details of each field.
- app_integrity_state: This field indicates if a Meta Quest application was legitimately installed through the Meta Quest App Store. Legitimate installations will return
StoreRecognized
, illegitimate installations will return NotRecognized
, and unidentifiable applications will return NotEvaluated
. - package_cert_sha256_digest: This field contains the SHA2-256 hashes of all package certificates’ signatures. It may contain multiple hashes if there is more than one certificate (i.e., a certificate chain) that application developers use to sign their application package. Application servers can compare the returned hash(es) against their known certificate signature hashes.
- package_id: This field is the package name provided by application developers. It follows the format of
com.example.name123
. - version: This field is the package version provided by application developers.
The device_state
section provides information regarding the attestation of the device. Below is a detailed explanation of each field:
device_integrity_state
: This field provides insights into the trustworthiness of a Meta Quest device. It can take on three possible values:
Advanced
: The device has passed an advanced integrity security check, including hardware-backed integrity checks and comprehensive OS property security validations.Basic
: The device meets basic hardware-backed security integrity standards, but some Android OS property security checks remain unverified.NotTrusted
: The device did not pass our basic security integrity checks, such as having an unlocked bootloader.
unique_id
: This identifier is unique to each application, ensuring different apps will have distinct IDs for the same device. Generated as a random string, it serves to uniquely identify a device for a specific app. To uphold user privacy and deter long-term device tracking, the unique_id refreshes every 30 days. However, within this monthly period, apps can leverage this ID for hardware banning actions.
Note: When processing token claims, you must ensure that new claims do not disrupt the functionality of your app during runtime. Therefore, it is important to carefully plan and adjust your token handling logic accordingly.
When necessary, you can use the Attestation API to band specific Meta Quest Devices from access your app. With this feature you can set the duration of the device ban, giving you control over how long a device is banned from accessing your app.
A device ban can be a key part of your overall anti-abuse strategy and can help protect your app and user community from:
- Unsanctioned modifications and security breaches: Determines whether the Attestation API has detected an unsanctioned modified app (e.g. a pirated app).
- Severe and repeated abuse: Determine whether you’ve identified severe and repeated abuse from a specific Meta Quest device.
Be sure to use the Attestation API’s device ban feature together with your in-app reporting system, as part of your overall app moderation strategy. (Check this
Content Moderation & Reporting Compliance course for guidance on how to set up a moderation program and comply with our reporting and governance requirements.)
Note: While we provide the infrastructure for device bans, Meta is not responsible for the ban that you issue. You are responsible for using the device ban feature in a conscientious manner. Misuse of the Attestation APi’s device ban feature is a violation of our
Platform Abuse Policy and may result in enforcement action.
The device ban feature functions by having the application service inform the Meta Attestation Server of the specific device to be banned.
- The device is identified by a pseudo unique_id, which is extracted from the current attestation token under the device_state section.
- Your application server sends a server-to-server request to the Meta Attestation Server, specifying the device’s unique_id and the duration of the ban.
- If the request is successful, the Meta Attestation backend marks the device as banned for the application.
- Subsequent attestation tokens for the device will then include a device_ban section reflecting this ban status.
The following diagram shows a complete call flow of this process:
The enforcement of the device ban takes place on the application server side.
- Once your application server receives an attestation token, it checks if the token contains a device_ban section
is_banned
:true. - If the condition is met, you decide how you want to enforce the ban.
The following call graph of this enforcement process is shown below:
Utilizing the device ban feature does not require a change in your current implementation of the Attestation API. There will be adjustments to the returned information when calling the Attestation API, and new server-to-server requests are accessible to ban a device as well as check whether a given device is currently banned or not.
Attestation Token Device Ban status Follow the
Token Verification steps to do a normal Attestation Token query. Inside the
token claims section the returned result will have a
device_ban section only of the device is banned. Otherwise the
device_ban section will be omitted.
The following is an example of an attestation token for a device with no ban:
{
"request_details": {
"exp": 1684606153,
"nonce": "JMxMPp1H6kCxGzPRsjKFLw==",
"timestamp": 1684519753
},
"app_state": {
"app_integrity_state": "StoreRecognized",
"package_cert_sha256_digest": [
"c8a2e9bccf597c2fb6dc66bee293fc13f2fc47ec77bc6b2b0d52c11f51192ab8"
],
"package_id": "com.example.name123",
"version": "1"
}
"device_state": {
"device_integrity_state": "Advanced",
"unique_id": "442ab6392117e89759e8872852de0495aa58f22bd8a1253123123",
}
}
The following is an example of an attestation token for a device that is currently banned:
{
"request_details": {
"exp": 1684606153,
"nonce": "JMxMPp1H6kCxGzPRsjKFLw==",
"timestamp": 1684519753
},
"app_state": {
"app_integrity_state": "StoreRecognized",
"package_cert_sha256_digest": [
"c8a2e9bccf597c2fb6dc66bee293fc13f2fc47ec77bc6b2b0d52c11f51192ab8"
],
"package_id": "com.example.name123",
"version": "1"
}
"device_state": {
"device_integrity_state": "Advanced",
"unique_id": "442ab6392117e89759e8872852de0495aa58f22bd8a1253123123",
}
"device_ban": {
"is_banned": true
"remaining_time_in_minute": 360000
}
}
Ensure you set the duration of your device ban correctly to avoid irreversible actions if the device becomes untraceable. The returned unique_id in the “device_state” section is linked to the specific device returning the attestation token and the application requesting the token. This unique_id changes every 30 days to protect user privacy. During that 30 day window you can manually use the unique_id to reverse a device ban by setting is_banned:false through a server-to-server request. You can also adjust the duration of the ban.
However, once 30 days have passed and the unique_id changes, you won’t be able to trace the same device with the previous ID which means the device will have to wait through the amount of time originally set in the ban.
A user’s device can only be banned through a server-to-server request via an endpoint.
- Query the attestation token following this guide to retrieve the unique_id under the device_state section of the token claims.
- Go to the Developer Dashboard and select your app.
- In the left-side navigation, select Development > API.
- In the App Credentials section, get your access token. It uses the following format: OC/App_ID/App_Secret.
In the following endpoint, replace:
unique_id
with the pseudo unique ID that is found in the attestation token in step 1 is_banned
with either true if the device should be banned or false if notremaining_time_in_minute
with an integer value between 0 and 52560000 (i.e. 100 years) inclusive of how long the device should be banned for
Note: If is_banned
is set to false, this parameter is still needed but will be disregarded.
access_token
parameter with the access token found in step 5
https://graph.oculus.com/platform_integrity/device_ban?method=POST&unique_id=<unique_id>&is_banned=<is_banned>&remaining_time_in_minute=<remaining_time_in_minute>&access_token=<access_token>
Upon a successful device ban request, you will receive a success response from the Attestation Server, as shown below:
If the unique_id is invalid, it will return an error message similar to the one shown below:
{
"error": {
"message": "There is no record of this Unique ID. Please check that the Unique ID matches what is given in the Attestation Token and try again. Note that Unique IDs will change every 30 days for each application and device, make sure the Unique ID is up to date.",
"type": "OCApiException",
"code": 1,
"error_data": {
},
"error_subcode": 1614026,
"fbtrace_id": "AUhV19OFsDqs6gTXn7zVybm"
}
}
If the unique_id does not match the application information stored in the access_token, it will return an error message similar to the one shown below:
{
"error": {
"message": "The App ID of the Access Token does not match the Unique ID, please check the Unique ID/Access Token and try again",
"type": "OCApiException",
"code": 1,
"error_data": {
},
"error_subcode": 1614027,
"fbtrace_id": "AHBbS8rEDcgajcgpQcqtVO1"
}
}
If the remaining_time_in_minute input is not within bounds [0, 52560000], it will return an error message similar to the one shown below:
{
"error": {
"message": "The Remaining Time In Minute field is invalid. Please check that the time inputted is between 0 and 52560000.",
"type": "OCApiException",
"code": 1,
"error_data": {
},
"error_subcode": 1614028,
"fbtrace_id": "AtFBCvCIb4IXSGvvc4GudY8"
}
}
How to query Device Ban status The device ban status can only be queried through a server-to-server request via an endpoint.
- Query the attestation token following this guideto retrieve the unique_id under the device_state section of the token claims
- Go to the Developer Dashboard.
- Select your application.
- In the left-side navigation, select Development > API.
- In the App Credentials section, copy your access token. It uses the format OC/App_ID/App_Secret.
In the following endpoint, replace:
unique_id
with the pseudo unique ID that is found in the attestation token in step 1is_banned
with either true if the device should be banned or false if notremaining_time_in_minute
with an integer value between 0 and 52560000 (i.e. 100 years) inclusive of how long the device should be banned for
Note: If is_banned
is set to false, this parameter is still needed but will be disregarded.
access_token
parameter with the access token found in step 5
https://graph.oculus.com/platform_integrity/device_ban_status?unique_id =<unique_id>&access_token=<access_token>
Upon a successful device ban request, you will receive a success response from the Attestation Server, similar to the one shown below:
{
"data": [
{
"message": "Success",
"is_banned": true,
"remaining_time_in_minute": 12
}
]
}
If the unique_id is invalid, it will return an error message similar to the one shown below:
{
"error": {
"message": "There is no record of this Unique ID. Please check that the Unique ID matches what is given in the Attestation Token and try again. Note that Unique IDs will change every 30 days for each application and device, make sure the Unique ID is up to date.",
"type": "OCApiException",
"code": 1,
"error_data": {
},
"error_subcode": 1614026,
"fbtrace_id": "AUhV19OFsDqs6gTXn7zVybm"
}
}
If the unique_id does not match the application information stored in the access_token, it will return an error message similar to the one shown below:
{
"error": {
"message": "The App ID of the Access Token does not match the Unique ID, please check the Unique ID/Access Token and try again",
"type": "OCApiException",
"code": 1,
"error_data": {
},
"error_subcode": 1614027,
"fbtrace_id": "AHBbS8rEDcgajcgpQcqtVO1"
}
}
For a more personalized integration, you can adjust the attestation API according to your needs and use cases.
To configure the Attestation API: Visit the
Meta Quest Developer Dashboard. Select your application. In the left-side navigation, click on “Settings”. You’ll find the “Attestation API” tab where the following configurations can be made:
Device Integrity State Bypass
: This is enabled by default. When activated, Meta’s internal testing devices will always have the device_integrity_state
within the device_state
of the attestation token set to Advanced
. This is particularly useful for your application server to block services on NotTrusted
devices. By using this bypass, Meta’s internal testing team can efficiently assist you, ensuring seamless testing of your applications on Meta’s devices, especially during preliminary build release assessments.Token Expiration Duration
: This setting determines the lifespan of an attestation token. The default expiration duration is 24 hours. Shorter durations enhance security by reducing the time it takes to detect device and app compromises, but they require more frequent attestation checks. Conversely, longer durations decrease the load on apps but increase the risk of extended unauthorized access in the event of a compromise.
To maintain optimal performance and ensure fair use, there are rate limits on the number of API calls a device can make. This limit is 100 requests per hour and 200 requests per day for each device. You must therefore design your application around these rate limits. Each attestation token obtained through this APi will remain valid for a period of 24 hours.
To prevent hitting the rate limits, we recommend that you implement token caching in your application. Saving and reusing tokens within their validity period will reduce the frequency of API calls and minimize the risk of reaching the rate limit.