Foundation
This section provides a detailed reference for backend integrations. If you able to use one of our Backend API Quickstarts then you may find this simpler.
We recommend that you read the Frontend Integration section first, as this provides the context for the backend integration. The backend integration is typically performed after the frontend integration is complete.
Requirements
To protect a backend API with Approov it is necessary to check that incoming requests have a valid Approov token. This might be required on all endpoints or just on some that demand additional security. Typically, token checking will be performed in some middleware layer of the backend rather than be implemented in the business logic.
Approov token checking is often performed in addition to user authorization, as an orthogonal activity. User authentication and authorization ensure that a known user is invoking the software making the request. Approov checking ensures that the software making the request is actually what it claims to be.
Approov uses standard JWTs as discussed in the previous section. This makes the backend API checking straightforward since almost all backend languages and technologies support JWT checking. To check an Approov JWS, the account secret key must be made available to the backend code. The token will be signed with the HS256 profile, and the shared symmetric account secret key is needed to perform the check. Libraries for working with JWTs are available in most common languages, with many listed on the JWT homepage.
There are essentially three cases which should cause a request to be rejected:
- Invalid Token: If the Approov token is simply missing or is not a correctly formatted JWT then this indicates some spoofed request and should be rejected.
- Incorrectly Signed Token: If the token is not correctly signed then it must be rejected. Obviously an attacker can synthesize an incorrectly signed token. An app is also provided with an incorrectly signed token if the attestation has been rejected. Note it is important to ensure that the signing method of the JWT is
HS256. - Expired Token: Any token that has expired must be rejected, as under normal operation this should never happen. The app should never cache the Approov token, and the Approov SDK never provides a token that has expired. A small grace period is built into the token lifetime to allow for transmission and propagation delays in the backend systems. It is important to make sure that the server checking the tokens is correctly time synchronized.
Note that the actual behavior for an invalid token does not have to be rejection, although this is the typical approach. An invalid token event could simply be used for logging or as a signal to require some additional authentication and security checks on a particular user.
We recommend that the backend integration has a dynamic facility to enable or disable the Approov token check. This allows traffic to be passed through without the check in case of some emergency situation. Moreover, you may wish to collect logging for events where invalid Approov tokens are being provided.
Account Secret Key Export
The account secret key can be obtained with the following command:
approov secret -get base64
An admin role required to gain access to the account secret key.
This example displays the secret as a base64 encoded string, representing a 512-bit value that is randomly assigned to the account during signup. The value of the secret must be protected with extreme care. The number of individuals with exposure to the value of the secret should be minimized and it should never be checked into a source control system. Ideally, it should only be made available to the runtime environment of the backend servers. Note that if base64url is used as a parameter to the command then the secret can be obtained in base64url encoded format, which may be needed by some backend integrations.
Note that it is also possible to obtain the secret in raw form, as discussed here.
If you wish to output the secret value, with no additional text, you may use the -plain option. This allows the output to be easily piped into other command line tools, so that generic scripts can be easily developed that extract and operate on the account secret key. Of course, you should never include your administration token in such a script directly.
If you wish to export the secret key that is used for JWE tokens (of the account type and using the A256GCMKW algorithm) then the -jwe option can be used as follows:
approov secret -jwe -get base64
Account Secret Key JWK Export
The account secret key may also be exported in JSON Web Key (JWK) format as follows:
approov secret -getJWK secret.jwk
This writes the JWK to the file secret.jwk with content like the following:
{
"kty": "oct",
"use": "sig",
"k": "dEitzKUYJLQ…",
"alg": "HS256"
}
The JWK format is useful for certain backend systems that accept this format directly. Usage with JWKs is also often associated with the requirement for a Key ID property in tokens. This needs to be set prior to getting the JWK so that the kid property is listed.
It is also possible to export it in the JSON Web Key Set format, for systems that require that format. In this case the single account secret key is included in the keys map.
If the -jwe option is used then the secret key used for JWEs is provided, rather than the one for JWSs.
Generating Example Tokens
It is possible to generate example Approov tokens that have the same format as those generated for a mobile app. The API domain, for which the token is being generated, must be provided. For example:
approov token -genExample my.api.io
This generates the example token as the output. The result is a valid token that lasts for one hour. The extended expiry time of these tokens provides for longer debug sessions using a single token. For example, the token can be used in synthetic queries made to a backend API to check that it is working correctly. There is no need to know the actual account secret key to use this facility as the token is automatically signed with the correct secret.
If an API domain is provided that has not been added for the account then no token can be generated. If the API domain is associated with encrypted JWE tokens then one is generated, to allow verification of the JWE decryption logic in the backend.
The example tokens contain a fake IP address and device ID that are in the correct format. A -setDataHashInToken option is also provided for testing the Token Binding feature. You can decode the example Approov token to see what it contains as follows:
approov token -check eyJhbGciOiJIUzI1N…
By default a valid Approov token is generated. But it is also possible to generate an invalid token to test the backend API handling with such tokens. For instance:
approov token -genExample my.api.io -type invalid
Changing the Account Secret Key
The account secret key can be changed with the following command:
approov secret -change
This requires an admin role and a confirmation of the operation:
WARNING: updating the account secret key will have an immediate impact on your apps in production
ATTENTION: If you wish to continue then please type YES and return: YES
If confirmed then the new account secret key value is output as follows:
new base64 encoded account secret key is VcXl2IgVUgz…
If the account secret key is changed then this has an impact in production within 30 seconds for newly issued tokens. This means that for a period of up to 5 minutes the backend API may see a combination of tokens signed with the old secret (if they have been cached and reused by the app SDKs) and new ones signed with the new secret (for freshly obtained tokens).
In general it is hard for a backend integration to deal with two different secret values, so we recommend the following procedure if rotating the account secret key:
- Disable Approov token checking on the backend API. This will let any traffic through for a short period of time,
- Execute the command to update the account secret key and copy the new value.
- Integrate the new account secret key into the backend API.
- Re-enable Approov token checking. Assuming this process takes more than 5 minutes, all tokens in use will be using the new account secret key.
Raw Account Secret Key
There are certain specific backend JWT checking libraries that require the secret to be specified as a raw string (they have no facility to provide it in an encoded form such as base64). To support this, a feature is provided to get the secret in its raw format. However, the initial secret generated for your account will almost certainly contain some bytes that are not printable and therefore cannot easily be added to a string. Thus, if you need the secret in its raw form, you must first change it and include the special -forcePrintable option as follows:
approov secret -change -forcePrintable
It is then possible to obtain the raw secret value as follows:
approov secret -get raw
Use of this option reduces the entropy of the secret and so you should only use this facility if there is no option but to use a JWT library that requires the secret in this format. Typically you should specify the secret in an encoded format rather than a raw string.
Key IDs
It is possible to associate a key ID with all Approov tokens signed with the account secret key. You may arbitrarily choose the key ID as follows:
approov secret -setKeyID your-keyid
Once this is set then all issued JWS tokens will contain a kid field in the header with the value provided. The currently set value can be obtained with:
approov secret -getKeyID
Inclusion of a key ID can be removed with:
approov secret -clearKeyID
The addition of a key ID does not impact tokens issued for certain Approov managed domains, such as shapes.approov.io nor for domains using encrypted JWE tokens or tokens signed using a keyset key.
Get backend configuration
Get the current backend configuration for the account, which is written to the provided pathname. This single file contains everything required by an Approov backend integration to verify and decode the tokens issued for your account: the ARC encoding secrets, the default account secrets, the pass and fail key sets, the ARC flag mappings, and the set of API domains along with the key each one uses. For example:
approov secret -getBackendConfig <pathname>
This will output the backend configuration to the file <pathname> with content like the following:
{
"currentArcEncodingSecret": "FSx]_QA?qw@(be,m2K0-U",
"pendingArcEncodingSecret": ":Ig~P?#M@vljSdY4D8:Ag",
"previousArcEncodingSecrets": [
{
"secret": "K2+_]l3k5.%=-3TW*DK|f",
"timestamp": "2026-04-08T12:19:30+01:00"
},
{
"secret": "LtM#+q{RA@:-5b3m*;g7G",
"timestamp": "2026-03-10T16:29:41Z"
}
],
"defaultPassJWKS": {
"keys": [
{
"alg": "A256GCMKW",
"k": "...<default account JWE secret if enabled>...",
"kid": "_A256GCMKW",
"kty": "oct",
"use": "enc"
},
{
"alg": "HS256",
"k": "...<default account JWS secret>...",
"kid": "_HS256",
"kty": "oct",
"use": "sig"
}
]
},
"passJWKS": {
"keys": [
{
"alg": "HS256",
"k": "...<different from fail keyset>...",
"kid": "test-key",
"kty": "oct",
"use": "sig"
}
]
},
"failJWKS": {
"keys": [
{
"alg": "HS256",
"k": "...<different from pass keyset>...",
"kid": "test-key",
"kty": "oct",
"use": "sig"
}
]
},
"arcFlagMappings": {
"1": "debug",
"10": "ios-simulator",
"...": "and the rest"
},
"apis": {
"myapi_keyset.com": {
"kid": "test-key",
"isPassKeyPresent": true,
"isFailKeyPresent": true,
"comment": "API uses key ID present in both pass and fail key sets"
},
"myapi_default.com": {
"kid": "",
"isPassKeyPresent": true,
"isFailKeyPresent": false,
"comment": "API uses default account secret with signed(HS256) form, no fail secret available"
},
"shapes.approov.io": {
"kid": "",
"isPassKeyPresent": false,
"isFailKeyPresent": false,
"comment": "API uses Approov managed consumable API, no exported secrets available"
}
}
}
The individual properties of the backend configuration are as follows:
currentArcEncodingSecret,pendingArcEncodingSecretandpreviousArcEncodingSecrets: The ARC encoding secrets used to decode thearcclaim of tokens. The current secret decodes newly issued tokens, the pending secret is provided if one has been staged, and each previous secret is listed with the timestamp it was archived so that tokens issued before recent rotations can still be decoded.defaultPassJWKS: The default account secret, or secrets, presented as a JWKS (explained below). This is the only command that exports the default account secrets in JWKS form.passJWKSandfailJWKS: The pass and fail key sets. ThepassJWKSholds the keys used to sign or encrypt tokens for passing attestations, and thefailJWKSholds the corresponding keys for failing attestations. Each entry shares the samekidand algorithm across both key sets but uses different key material, which allows a backend to distinguish genuine passing tokens, genuine failing tokens, and tokens that were not minted by Approov.arcFlagMappings: The ARC flag mappings that translate the numeric flags in a decodedarcclaim into device property names (truncated in the example above).apis: The set of account API domains and, for each, thekidin use, whether a pass key (isPassKeyPresent) and a fail key (isFailKeyPresent) are available in the configuration, and acommentexplaining the entry. An emptykidindicates that the API uses the default account secret. This map is provided for informational purposes only, showing which secrets were in use by which APIs at the point the configuration was obtained. A backend should not use it to select the verification key, because that assignment can change (for example during a secret rotation). Instead, the backend should trust thekidin the token header and use the corresponding key set entry, verifying theaudclaim of the token if there are multiple APIs with different assigned key IDs.
The defaultPassJWKS holds the default account secrets in JWKS form using a custom key ID scheme that reflects the way default account secrets are managed. There is always an HS256 signing (use: sig) entry for the default JWS secret and, only if JWE tokens are enabled for the account, an A256GCMKW encryption (use: enc) entry for the default JWE secret. Specifying a key ID for the default account secrets is optional and is always identical, so to keep the two entries distinct the -getBackendConfig command constructs their kid values by appending a suffix to whatever key ID has been set for the default secret: the HS256 entry appends _HS256 and the A256GCMKW entry appends _A256GCMKW. This means that any backend API accepting the default account secrets should append the appropriate suffix to the key ID it extracts from the token in order to select the correct entry from the configuration. To minimise overhead, your backend API should know whether it is valid to accept the default account secrets or not and it should prefer to find a keyID in the passJWKS before trying the defaultPassJWKS.
It also reports which of the account's API domains are supported by the backend configuration for ARC decoding, as follows:
backend configuration written to file <pathname>
the backend configuration supports ARC decoding from passing tokens for the following APIs:
- myapi_keyset.com (Key ID: test-key)
- myapi_default.com (Key ID: )
the backend configuration supports ARC decoding from failing tokens for the following APIs:
- myapi_keyset.com (Key ID: test-key)
the backend configuration does not support ARC decoding for the following APIs:
- shapes.approov.io (Reason: API uses Approov managed consumable API, no exported secrets available)
Rotating Backend Secrets
When you rotate the secret used to verify tokens for an API, both the old and new secrets are in use for a short period. This is because running apps may still hold cached tokens signed with the old secret (for up to the token lifetime, typically 5 minutes) while newly issued tokens use the new secret. The backend configuration is designed to make this transition safe, since it can contain both the old and new secrets at once.
If you are currently verifying tokens with the default account secret then we recommend migrating to a key set on the first rotation. Using a key set means the key ID (kid) changes with each rotation, so both the old and new secrets can be present in the configuration simultaneously and it is always possible to verify tokens throughout the transition.
The general procedure to rotate the secret for an API is as follows:
- Add a new key, with a new
kid, to the key set. Download the new backend configuration (which now contains both the new and the old secrets) and deploy it to production. Ensure your backend integration is set to permit any key present in the key set. If you have multiple APIs using different secrets then enable audience inclusion withapproov policy -setAudience onand verify theaudclaim of the token to select the correct API. - Once the new configuration is live, change the API to issue tokens signed with the new key using
approov api -add <domain> -keySetKID <new-kid>. Make sure to also include any other flags that were previously set for the API, as adding it again replaces its configuration. - Wait for the API to stop receiving tokens signed with the old secret. Since most accounts have a token lifetime of 5 minutes, this period should be at least that long.
- Remove the old key from the key set (or, if you were rotating the default account secret and nothing else is using it, change the account secret key) so that tokens signed with the old secret will no longer verify.
- Finally, download the new backend configuration again and apply it to production so that the old secret is no longer accepted.