microg/GmsCore · error · RequestHandlingException
NOT_ALLOWED_ERR
NOT_ALLOWED_ERR
Error message
RP ID $rpId is a public suffix
What it means
RequestHandling.checkIsValid validates WebAuthn request parameters. It rejects an rpId that is itself a public suffix (e.g. 'com', 'co.uk') using Guava's InternetDomainName.from(rpId).isPublicSuffix, throwing RequestHandlingException(NOT_ALLOWED_ERR). Permitting a public-suffix RP ID would let a single credential scope span every site under that suffix, which the WebAuthn spec forbids.
Source
Thrown at play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/RequestHandling.kt:176
}
}
suspend fun RequestOptions.checkIsValid(context: Context, origin: String, packageName: String?) {
val allApplicableFacetIds = hashSetOf<String>()
if (origin.startsWith("https://")) {
allApplicableFacetIds.add(origin)
val originUri = origin.toUri()
// The RP ID must be equal to the origin's effective domain, or a registrable domain
// suffix of the origin's effective domain: For origin https://login.example.com:1337,
// login.example.com and example.com are valid rpId,
// but m.login.example.com and com aren't valid
// => We don't check topDomainOf(originUri.host) against topDomainOf(rpId), because:
// 1. rpId m.login.example.com would be a valid rpId for https://login.example.com:1337
// 2. it excludes internal domains as topDomainOf requires a public FQDN
//
// Instead, we first check that rpId is valid, then if it matches the origin host
if (runCatching { InternetDomainName.from(rpId).isPublicSuffix }.getOrDefault(false)) {
throw RequestHandlingException(NOT_ALLOWED_ERR, "RP ID $rpId is a public suffix")
}
if (
originUri.host != rpId &&
originUri.host?.endsWith(".$rpId") != true
) {
throw RequestHandlingException(NOT_ALLOWED_ERR, "RP ID $rpId not allowed from origin $origin")
}
// FIXME: Standard suggests doing additional checks, but this is already sensible enough
} else if ((origin.startsWith("android:apk-key-hash:") || origin.startsWith("android:apk-key-hash-sha256:")) && packageName != null) {
allApplicableFacetIds.addAll(getAllFacetIdCandidates(context, packageName, origin))
val sha256facetId = allApplicableFacetIds.firstOrNull { it.startsWith("android:apk-key-hash-sha256:") }
?: throw RequestHandlingException(NOT_ALLOWED_ERR, "RP ID $rpId not allowed from origin $origin")
val fp = Base64.decode(sha256facetId.substring(28), HASH_BASE64_FLAGS).toHexString(":")
if (!isAssetLinked(context, rpId, fp, packageName)) {
throw RequestHandlingException(NOT_ALLOWED_ERR, "RP ID $rpId not allowed from origin $origin (expected fingerprint $fp)")
}
} else {
throw RequestHandlingException(NOT_SUPPORTED_ERR, "Origin $origin not supported")View on GitHub (pinned to 157c9d86ac)
Solutions
- Set rpId to a registrable domain you control (e.g. 'example.com' or 'login.example.com'), never a bare public suffix
- Pre-validate with Guava: InternetDomainName.from(rpId).isPublicSuffix / isTopPrivateDomain before issuing the request
- If hosting on a public-suffix platform, register a custom domain or use a subdomain you own as the RP ID
- Catch RequestHandlingException(NOT_ALLOWED_ERR) and show which rpId was rejected and why
Example fix
// before
val options = PublicKeyCredentialCreationOptions(rpId = "co.uk", ...)
// after
val domain = InternetDomainName.from("co.uk")
require(!domain.isPublicSuffix) { "rpId must be a registrable domain, not a public suffix" }
val options = PublicKeyCredentialCreationOptions(rpId = "auth.example.co.uk", ...) Defensive patterns
Strategy: validation
Validate before calling
import com.google.common.net.InternetDomainName
fun rpIdIsRegistrable(rpId: String): Boolean = runCatching {
InternetDomainName.from(rpId).let { !it.isPublicSuffix }
}.getOrDefault(false)
if (!rpIdIsRegistrable(rpId)) failWith(NOT_ALLOWED_ERR, "rpId must not be a public suffix") Type guard
fun String.isValidRpId(): Boolean = runCatching {
InternetDomainName.from(this).isTopPrivateDomain || InternetDomainName.from(this).isUnderPublicSuffix
}.getOrDefault(false) Try / catch
try {
fidoClient.handle(options)
} catch (e: RequestHandlingException) {
if (e.code == NOT_ALLOWED_ERR) showRpIdConfigError(e.message)
else throw e
} Prevention
- Never configure an RP ID that is a bare public suffix (com, org, co.uk)
- Use Guava InternetDomainName.isTopPrivateDomain as a pre-flight check
- On shared public-suffix hosting, register your own domain or subdomain for the RP ID
When it happens
Trigger: Calling the FIDO register/sign pipeline with an rpId set to a registrable-suffix-only domain such as 'com', 'org', or 'co.uk' in PublicKeyCredentialCreationOptions/RequestOptions (or their browser wrappers) for an https origin.
Common situations: Developers who read the RP ID from a cookie domain set as a public suffix; misconfigured relying parties that store only the TLD+suffix in config; test harnesses using 'localhost'-adjacent or placeholder suffix domains; providers on shared public-suffix platforms (e.g. *.github.io) using the platform suffix instead of their own subdomain.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- DATA_ERR
- Transport ${transport} not supported
- Attachment ${attachment} not supported
- Attestation conveyance preference ${attachment} not supporte
- No response set.
AI-assisted analysis of microg/GmsCore@157c9d86ac (2026-09-06).
Data as JSON: /api/errors/33afb87f2d5dcb12.
Report an issue: GitHub.