Verify attestation
Validates an XitAttestationToken produced by the device.
POST /api/v1/attestation/verify
Authentication
Requires a valid API key in the X-Api-Key header. See Authentication.
Request
The body is JSON. Byte-array fields are base64-encoded.
| Field | Type | Presence | Description |
|---|---|---|---|
attestationToken | string (base64) | required | The CBOR-encoded XitAttestationToken produced by the device. |
sessionReference | string | conditional | Session identifier returned by POST /attestation/init. Required in the session approach; must be absent in the sessionless approach. |
deviceNonce | string (base64) | conditional | Device-nonce part. Optional in the session approach (only when the integration uses a device nonce). Must be absent in the sessionless approach. |
expectedNonce | string (base64) | conditional | The full final nonce. Required in the sessionless approach; must be absent in the session approach. |
Exactly one of sessionReference or expectedNonce must be present. Sending both, or neither, is rejected as a malformed request.
Variant: session approach
curl https://attestation.example.com/api/v1/attestation/verify \
--request POST \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: xma_SampleKey' \
--data '{
"attestationToken": "[base64 CBOR token]",
"sessionReference": "019dd9b7-6c0f-755d-be98-2e82a6d067a0",
"deviceNonce": "HB/xSNrFd1A6jobTa13ZohuajtBKkF6ORQQPAFqpd9Y="
}'
deviceNonce is omitted when the integration does not use a device nonce.
Variant: sessionless approach
curl https://attestation.example.com/api/v1/attestation/verify \
--request POST \
--header 'Content-Type: application/json' \
--header 'X-Api-Key: xma_SampleKey' \
--data '{
"attestationToken": "[base64 CBOR token]",
"expectedNonce": "8wY7WRuvpD8CtNuMuQeJV6nt9ToZxn9JQ2NBD8GEo8A="
}'
Response
Success — 200 OK, isValid: true
{
"isValid": true,
"googleTokenDetails": {
"packageName": "com.example.app",
"deviceIntegrityVerdicts": ["MEETS_DEVICE_INTEGRITY", "MEETS_BASIC_INTEGRITY"],
"timestampMillis": 1714485269871
},
"statusCode": 0
}
{
"isValid": true,
"appleTokenDetails": {
"keyIdentifier": "iLJVbLGq4...3uhP/I=",
"appId": "ABCDE12345.com.example.app",
"environment": "Production",
"assertionCounter": 7
},
"statusCode": 0
}
| Field | Type | Description |
|---|---|---|
isValid | bool | true when the token passed all checks. |
googleTokenDetails | object | Present when device.service is "gms". Omitted otherwise. |
googleTokenDetails.packageName | string | Package name asserted by the Play Integrity token. Compare against the package the RP expects. |
googleTokenDetails.deviceIntegrityVerdicts | string[] | Verdicts from Play Integrity (e.g. MEETS_DEVICE_INTEGRITY, MEETS_BASIC_INTEGRITY, MEETS_STRONG_INTEGRITY). |
googleTokenDetails.timestampMillis | long | Token timestamp in milliseconds since the Unix epoch. |
appleTokenDetails | object | Present when device.service is "apple". Omitted otherwise. |
appleTokenDetails.keyIdentifier | string | App Attest key ID, base64. Echoed from the input token. |
appleTokenDetails.appId | string | The bound app ID extracted from the attestation (<TeamID>.<bundle-id>). Compare against the app the RP expects. |
appleTokenDetails.environment | string | "Development" or "Production". |
appleTokenDetails.assertionCounter | uint | Monotonic per-key assertion counter from App Attest. Validated server-side — the API rejects any token whose counter is not strictly greater than the previous one for the same key. |
statusCode | uint | 0 on success. |
The Attestation API enforces app/package binding, environment, and assertion-counter monotonicity against its own configured allowlist. The token-details fields are exposed for telemetry and for any RP-specific policy that goes beyond the API's defaults — for example, requiring MEETS_STRONG_INTEGRITY rather than just MEETS_DEVICE_INTEGRITY for high-value operations.
Token rejected — 200 OK, isValid: false
{
"isValid": false,
"statusCode": 0
}
The HTTP status is 200 and statusCode is 0 because the call itself completed normally — the token simply did not pass validation. The RP must deny the protected operation.
Reasons for isValid: false include (the response does not disclose which):
- The
attestationTokencould not be parsed as a validXitAttestationToken. - In the session approach: the
sessionReferencedoes not exist, has already been consumed, or has expired. (Note: a session is consumed even when validation fails, so a subsequent retry with the samesessionReferencealways returnsisValid: false.) - The final nonce reconstructed by the API does not match the nonce embedded in the token.
- Platform-specific validation failed (signature, certificate chain, app binding, integrity verdict, etc.).
Specific failure reasons are recorded in server-side logs.
Error responses
| HTTP status | statusCode | Cause |
|---|---|---|
400 Bad Request | 0x10011001 | Malformed request: attestationToken missing, neither sessionReference nor expectedNonce provided, or both provided. |
401 Unauthorized | — | X-Api-Key header missing. |
403 Forbidden | — | API key invalid, unknown, or revoked. |
500 Internal Server Error | 0x10011000 | Unexpected failure (database, validator lookup, platform verification crash). |
{
"statusCode": 268505089,
"errorMessage": "Either SessionReference or ExpectedNonce must be provided."
}
Status code reference
statusCode (hex) | statusCode (decimal) | Meaning |
|---|---|---|
0x00000000 | 0 | Success — applies to both isValid: true and isValid: false responses. |
0x10011000 | 268505088 | Internal error. |
0x10011001 | 268505089 | Invalid request. |
Authorization decision
The RP grants the protected operation only when both of the following hold:
- HTTP status is
200. isValidistrue.
Optionally, the RP may also enforce its own additional policy on top — for example, requiring a specific integrity verdict — by inspecting the token-details fields. App binding, environment, and counter monotonicity are already enforced by the API and do not need to be re-checked.
Anything else — isValid: false, HTTP 4xx, HTTP 5xx — must result in the RP denying the operation.