Policy configuration examples
The policies control the client's behavior, the allowed authentication algorithms and the FIDO UAF attestation types. Configuring the policies correctly requires some basic knowledge of the information needed to be included in the policy files.
The examples below provide complete and working policy examples which can be adapted based on specific needs.
FIDO UAF policies work hand-in-hand with the FIDO UAF Metadata.
- The metadata defines the types of authenticators and their capabilities.
- The policy defines which authenticators are allowed for a specific operation.
Application Attestation
nevisFIDO can attest that the FIDO UAF client is a trusted application (see Application Attestation for details). To enable this feature you must add an extension on each authenticator for which you want to perform application attestation. The extension is different for Android and for iOS.
If no extension is defined, then it is assumed that no application attestation must be done.
Android Application Attestation Extension
The extension to require application attestation for Android authenticators is the following:
{
"id": "ch.nevis.auth.fido.uaf.android-app-attestation-policy",
"data": "{ \"allowed_package_names\": [ \"my.application.packagename\" ] }"
}
| Attribute | Description | Optional |
|---|---|---|
id | The extension identifier. Its value must be ch.nevis.auth.fido.uaf.android-app-attestation-policy | false |
data | Stringified JSON, that can have a single attribute (allowed_package_names). | true |
data.allowed_package_names | An array of package names. Only the applications with the specified package names will be accepted. If not specified, all the applications defined in the facets will be accepted. | true |
iOS Application Attestation Extension
The extension to require Application Attestation for iOS authenticators is the following:
{
"id": "ch.nevis.auth.fido.uaf.ios-app-attestation-policy",
"data": "{ \"allowed_bundle_ids\": [ \"my.application.bundleid\" ], \"allowed_environments\": [ \"development\", \"production\" ] }"
}
| Attribute | Description | Optional |
|---|---|---|
id | The extension identifier. Its value must be ch.nevis.auth.fido.uaf.ios-app-attestation-policy | false |
data | Stringified JSON, it can have two attributes: allowed_bundle_ids and allowed_environments. | true |
data.allowed_bundle_ids | An array of bundle IDs. Only the applications with the specified bundle IDs will be accepted. If not specified, all the applications defined in the facets will be accepted. | true |
data.allowed_environments | An array of allowed environments, use development to allow applications using a development (or test) environment, and production for applications with production environment. If not specified, applications of any environment will be accepted. | true |
Android Key Attestation Verification Extension
The ch.nevis.auth.fido.uaf.android-key-attestation-verifications extension provides fine-grained control over Android key attestation checks when using Full Basic Attestation. It is added to the exts array of a MatchCriteria object.
The data field must be a JSON-stringified single configuration object or an array of configuration objects. When an array is provided, validation succeeds if the device matches any of the entries (OR logic) — enabling support for multiple device types (e.g. stock Android and GrapheneOS) within a single policy.
{
"id": "ch.nevis.auth.fido.uaf.android-key-attestation-verifications",
"fail_if_unknown": false,
"data": "{\"verifiedBootState\":[\"VERIFIED\"],\"requireDeviceLocked\":true,\"minKeymasterVersion\":2,\"keymasterSecurityLevel\":[\"TRUSTED_ENVIRONMENT\",\"STRONG_BOX\"]}"
}
| Field | Type | Description | Optional |
|---|---|---|---|
id | string | The extension identifier. Its value must be ch.nevis.auth.fido.uaf.android-key-attestation-verifications. | false |
fail_if_unknown | boolean | Whether to fail if the extension is unknown to the client. Should be set to false. | true |
data | string | JSON-stringified single configuration object or array of configuration objects (OR logic). | true |
data.keymasterSecurityLevel | string[] | Allowed Keymaster/KeyMint security levels. Allowed values: "TRUSTED_ENVIRONMENT", "STRONG_BOX". If omitted, the check is skipped. | true |
data.minKeymasterVersion | integer | Minimum required Keymaster/KeyMint version. Allowed values: 1, 2, 3, 4 (Keymaster) or 100, 200, 300, 400 (KeyMint). The actual version must be ≥ this value. If omitted, the check is skipped. | true |
data.verifiedBootState | string[] | Allowed verified boot states. Allowed values: "VERIFIED", "SELF_SIGNED", "UNVERIFIED". "FAILED" is never allowed. If omitted, the check is skipped. | true |
data.requireDeviceLocked | boolean | If true, the device must report itself as locked. If omitted or false, the check is skipped. | true |
data.verifiedBootKeys | string[] | Allowed boot key hashes (base16-encoded). Used for custom ROMs such as GrapheneOS. If omitted, the check is skipped. | true |
data.applicationPackageNames | string[] | Allowed Android application package names (e.g. "ch.nevis.accessapp"). If omitted, the check is skipped. | true |
data.minOsVersion | integer | Minimum required Android OS version as integer in the form AAABCC (e.g. 150000 for Android 15.0.0). If omitted, the check is skipped. | true |
data.minOsPatchLevel | integer[2] | Minimum required OS patch level as [year, month] (e.g. [2025, 9] for September 2025). If omitted, the check is skipped. | true |
data.permissiveModeEnabled | boolean | If true, attestation check failures are only logged as warnings and do not cause registration to fail. Defaults to false if omitted. See permissive mode. | true |
This extension is designed for Full Basic Attestation. The attestationTypes field in the MatchCriteria is not required — the presence of the extension itself signals that Full Basic is expected. If a device registers with Surrogate Basic Attestation when this extension is present, registration fails. This failure is not treated as a misconfiguration and can be suppressed by setting permissiveModeEnabled: true in the extension data.
Pre-built example policy files are provided in the conf/policy directory of the nevisFIDO installation. See the examples in the Policy Examples section below.
The use of this policy extension requires your mobile application to use Android Mobile Authentication SDK 4.4.0 and later.
Attestation Algorithms EC / RSA
Whilst iOS is consistently using Elliptic Curve as attestation validation algorithm, Android supports both RSA and Elliptic Curve. The usage of RSA is a legacy option, we recommend using Elliptic Curve by default as it is considered more secure.
The algorithm(s) to be used are identified by the authenticationAlgorithms entry:
2represents Elliptic Curve (ALG_SIGN_SECP256K1_ECDSA_SHA256_DER, recommended)9represents RSA (ALG_SIGN_RSASSA_PSS_SHA256_DER)
Without explicitly enforcing one algorithm via the policy, the Nevis Access App and SDK will use Elliptic Curve by default.
Full Basic / Surrogate Basic Attestation
The FIDO UAF attestation types are identified by the attestationType entry:
15879represents Full Basic Attestation15880represents Surrogate Basic Attestation
For an overview of the attestation types visit our concept guide. For Android key attestation verification checks, see the Android Key Attestation Verification Extension section above.
Full Basic Attestation is only supported for Android authenticators using the Nevis Mobile Authentication SDK and Access App. Customers using / implementing their own authenticators with their own metadata could still use Full Basic attestation depending on the client implementation.
To support both FIDO UAF Surrogate and Full Basic Attestation, the nevisFIDO component is using an adapted implementation parting from the UAF specification.
Policy Examples
By using the dynamic policy feature you have fine-granular control over what policy is to be used in which scenario.
Use this flexibility to differentiate between registration and authentication policies if needed. For example in migration scenarios, where you want existing user registrations to continue to work even when done with an algorithm or attestation type, that you do not want to use anymore for new registrations.
- Allowing all authenticators
- Requiring Application Attestation
- Enforcing FIDO UAF Surrogate Basic Attestation (Android)
- Enforcing FIDO UAF Full Basic Attestation (Android)
- Android Key Attestation – Strict (verified boot + TEE or StrongBox)
- Android Key Attestation – Strict StrongBox only
- Android Key Attestation – GrapheneOS support
- Allowing both RSA and EC algorithms (Android)
The following policy allows all available authenticators for both the Android and iOS platform:
{
"accepted": [
[
{
"aaid": ["F1D0#0001"]
}
],
[
{
"aaid": ["F1D0#0002"]
}
],
[
{
"aaid": ["F1D0#0003"]
}
],
[
{
"aaid": ["F1D0#0004"]
}
],
[
{
"aaid": ["F1D0#0005"]
}
],
[
{
"aaid": ["F1D0#1001"]
}
],
[
{
"aaid": ["F1D0#1002"]
}
],
[
{
"aaid": ["F1D0#1003"]
}
],
[
{
"aaid": ["F1D0#1004"]
}
],
[
{
"aaid": ["F1D0#1005"]
}
]
]
}
The following policy requires Application Attestation for all available authenticators for both the Android and iOS platform. Only the application with package name (or bundle ID in the case of iOS) com.my.application will be accepted. In the case of iOS, only the production applications will be allowed.
{
"accepted": [
[
{
"aaid": ["F1D0#0001"],
"exts":
[
{
"id": "ch.nevis.auth.fido.uaf.android-app-attestation-policy",
"data": "{ \"allowed_package_names\": [ \"ch.nevis.accessapp\" ] }"
}
]
}
],
[
{
"aaid": ["F1D0#0002"],
"exts":
[
{
"id": "ch.nevis.auth.fido.uaf.android-app-attestation-policy",
"data": "{ \"allowed_package_names\": [ \"ch.nevis.accessapp\" ] }"
}
]
}
],
[
{
"aaid": ["F1D0#0003"],
"exts":
[
{
"id": "ch.nevis.auth.fido.uaf.android-app-attestation-policy",
"data": "{ \"allowed_package_names\": [ \"ch.nevis.accessapp\" ] }"
}
]
}
],
[
{
"aaid": ["F1D0#0004"],
"exts":
[
{
"id": "ch.nevis.auth.fido.uaf.android-app-attestation-policy",
"data": "{ \"allowed_package_names\": [ \"ch.nevis.accessapp\" ] }"
}
]
}
],
[
{
"aaid": ["F1D0#0005"],
"exts":
[
{
"id": "ch.nevis.auth.fido.uaf.android-app-attestation-policy",
"data": "{ \"allowed_package_names\": [ \"ch.nevis.accessapp\" ] }"
}
]
}
],
[
{
"aaid": ["F1D0#1001"],
"exts":
[
{
"id": "ch.nevis.auth.fido.uaf.ios-app-attestation-policy",
"data": "{ \"allowed_bundle_ids\": [ \"ch.nevis.accessapp\" ], \"allowed_environments\": [ \"production\" ] }"
}
]
}
],
[
{
"aaid": ["F1D0#1002"],
"exts":
[
{
"id": "ch.nevis.auth.fido.uaf.ios-app-attestation-policy",
"data": "{ \"allowed_bundle_ids\": [ \"ch.nevis.accessapp\" ], \"allowed_environments\": [ \"production\" ] }"
}
]
}
],
[
{
"aaid": ["F1D0#1003"],
"exts":
[
{
"id": "ch.nevis.auth.fido.uaf.ios-app-attestation-policy",
"data": "{ \"allowed_bundle_ids\": [ \"ch.nevis.accessapp\" ], \"allowed_environments\": [ \"production\" ] }"
}
]
}
],
[
{
"aaid": ["F1D0#1004"],
"exts":
[
{
"id": "ch.nevis.auth.fido.uaf.ios-app-attestation-policy",
"data": "{ \"allowed_bundle_ids\": [ \"ch.nevis.accessapp\" ], \"allowed_environments\": [ \"production\" ] }"
}
]
}
],
[
{
"aaid": ["F1D0#1005"],
"exts":
[
{
"id": "ch.nevis.auth.fido.uaf.ios-app-attestation-policy",
"data": "{ \"allowed_bundle_ids\": [ \"ch.nevis.accessapp\" ], \"allowed_environments\": [ \"production\" ] }"
}
]
}
]
]
}
The following example forces the usage of FIDO UAF Surrogate Basic Attestation for Android authenticators:
{
"accepted": [
[
{
"aaid": ["F1D0#0001"]
},
{
"attestationTypes": [ 15880 ],
"assertionSchemes": ["UAFV1TLV"],
"authenticationAlgorithms": [ 2, 9 ]
}
],
[
{
"aaid": ["F1D0#0002"]
},
{
"attestationTypes": [ 15880 ],
"assertionSchemes": ["UAFV1TLV"],
"authenticationAlgorithms": [ 2, 9 ]
}
],
[
{
"aaid": ["F1D0#0003"]
},
{
"attestationTypes": [ 15880 ],
"assertionSchemes": ["UAFV1TLV"],
"authenticationAlgorithms": [ 2, 9 ]
}
],
[
{
"aaid": ["F1D0#0004"]
},
{
"attestationTypes": [ 15880 ],
"assertionSchemes": ["UAFV1TLV"],
"authenticationAlgorithms": [ 2, 9 ]
}
],
[
{
"aaid": ["F1D0#0005"]
},
{
"attestationTypes": [ 15880 ],
"assertionSchemes": ["UAFV1TLV"],
"authenticationAlgorithms": [ 2, 9 ]
}
],
[
{
"aaid": ["F1D0#1001"]
}
],
[
{
"aaid": ["F1D0#1002"]
}
],
[
{
"aaid": ["F1D0#1003"]
}
],
[
{
"aaid": ["F1D0#1004"]
}
],
[
{
"aaid": ["F1D0#1005"]
}
]
]
}
Enforcing Full Basic Attestation will lead to some Android Device Models not being able to register. Examples of devices which may not work are either:
- Old devices with outdated chipsets.
- New devices by Chinese manufacturers (for example Huawei) which are not using Google certified hardware.
When using our Nevis Mobile Authentication SDK, we highly recommend to use the #isPolicyCompliant() method of AuthenticatorSelectionContext and AccountSelectionContext to know if the device supports the policy, and to inform the end-users in case their device does not fulfill the security requirements enforced by strict policies.
The following example forces the usage of FIDO UAF Full Basic Attestation for Android authenticators:
{
"accepted": [
[
{
"aaid": ["F1D0#0001"]
},
{
"attestationTypes": [ 15879 ],
"assertionSchemes": ["UAFV1TLV"],
"authenticationAlgorithms": [ 2, 9 ]
}
],
[
{
"aaid": ["F1D0#0002"]
},
{
"attestationTypes": [ 15879 ],
"assertionSchemes": ["UAFV1TLV"],
"authenticationAlgorithms": [ 2, 9 ]
}
],
[
{
"aaid": ["F1D0#0003"]
},
{
"attestationTypes": [ 15879 ],
"assertionSchemes": ["UAFV1TLV"],
"authenticationAlgorithms": [ 2, 9 ]
}
],
[
{
"aaid": ["F1D0#0004"]
},
{
"attestationTypes": [ 15879 ],
"assertionSchemes": ["UAFV1TLV"],
"authenticationAlgorithms": [ 2, 9 ]
}
],
[
{
"aaid": ["F1D0#0005"]
},
{
"attestationTypes": [ 15879 ],
"assertionSchemes": ["UAFV1TLV"],
"authenticationAlgorithms": [ 2, 9 ]
}
]
]
}
This example enforces Full Basic Attestation and requires additional Android key attestation checks equivalent to the former strict verification level: verified boot state, locked bootloader, Keymaster version ≥ 2, and a security level of TEE or StrongBox.
The android-key-attestation-strict.json example policy file is shipped in the conf/policy directory of the nevisFIDO installation.
Enabling these checks further narrows the pool of supported devices. Set permissiveModeEnabled: true in the extension data to assess the impact before fully enforcing the policy. See permissive mode.
{
"accepted": [
[
{
"aaid": ["F1D0#0001"],
"exts": [
{
"id": "ch.nevis.auth.fido.uaf.android-key-attestation-verifications",
"fail_if_unknown": false,
"data": "{\"verifiedBootState\":[\"VERIFIED\"],\"requireDeviceLocked\":true,\"minKeymasterVersion\":2,\"keymasterSecurityLevel\":[\"TRUSTED_ENVIRONMENT\",\"STRONG_BOX\"]}"
}
]
}
]
]
}
This example enforces Full Basic Attestation and requires StrongBox as the only accepted Keymaster security level, in addition to verified boot, a locked bootloader, and Keymaster version ≥ 2. This is equivalent to the former strict-strongbox verification level.
The android-key-attestation-strict-strongbox.json example policy file is shipped in the conf/policy directory of the nevisFIDO installation.
StrongBox is only available on devices with dedicated secure hardware. This setting should only be enabled when a StrongBox-only security posture is required. Set permissiveModeEnabled: true in the extension data to assess the impact before fully enforcing the policy. See permissive mode.
{
"accepted": [
[
{
"aaid": ["F1D0#0001"],
"exts": [
{
"id": "ch.nevis.auth.fido.uaf.android-key-attestation-verifications",
"fail_if_unknown": false,
"data": "{\"verifiedBootState\":[\"VERIFIED\"],\"requireDeviceLocked\":true,\"minKeymasterVersion\":2,\"keymasterSecurityLevel\":[\"STRONG_BOX\"]}"
}
]
}
]
]
}
This example demonstrates OR logic: the first alternative matches standard verified-boot devices (TEE or StrongBox), while the second allows GrapheneOS devices that use a SELF_SIGNED boot key. Replace <base16-encoded-grapheneos-boot-key> with the actual base16-encoded GrapheneOS verified boot key hash.
The android-key-attestation-grapheneos.json example policy file is shipped in the conf/policy directory of the nevisFIDO installation.
{
"accepted": [
[
{
"aaid": ["F1D0#0001"],
"exts": [
{
"id": "ch.nevis.auth.fido.uaf.android-key-attestation-verifications",
"fail_if_unknown": false,
"data": "[{\"verifiedBootState\":[\"VERIFIED\"],\"requireDeviceLocked\":true,\"minKeymasterVersion\":2,\"keymasterSecurityLevel\":[\"TRUSTED_ENVIRONMENT\",\"STRONG_BOX\"]},{\"verifiedBootState\":[\"SELF_SIGNED\"],\"verifiedBootKeys\":[\"<base16-encoded-grapheneos-boot-key>\"],\"requireDeviceLocked\":true,\"minKeymasterVersion\":2,\"keymasterSecurityLevel\":[\"TRUSTED_ENVIRONMENT\",\"STRONG_BOX\"]}]"
}
]
}
]
]
}
The following example allows the Android authenticators to use both RSA or EC:
{
"accepted": [
[
{
"aaid": ["F1D0#0001"]
},
{
"authenticationAlgorithms": [ 2, 9 ],
"assertionSchemes": ["UAFV1TLV"]
}
],
[
{
"aaid": ["F1D0#0002"]
},
{
"authenticationAlgorithms": [ 2, 9 ],
"assertionSchemes": ["UAFV1TLV"]
}
],
[
{
"aaid": ["F1D0#0003"]
},
{
"authenticationAlgorithms": [ 2, 9 ],
"assertionSchemes": ["UAFV1TLV"]
}
],
[
{
"aaid": ["F1D0#0004"]
},
{
"authenticationAlgorithms": [ 2, 9 ],
"assertionSchemes": ["UAFV1TLV"]
}
],
[
{
"aaid": ["F1D0#0005"]
},
{
"authenticationAlgorithms": [ 2, 9 ],
"assertionSchemes": ["UAFV1TLV"]
}
]
]
}
Refer to the official FIDO UAF Policy and UAF Match Criteria specifications.