{"record":{"id":"33afb87f2d5dcb12","repo":"microg/GmsCore","slug":"not-allowed-err","errorCode":"NOT_ALLOWED_ERR","errorMessage":"RP ID $rpId is a public suffix","messagePattern":"RP ID \\$rpId is a public suffix","errorType":"error_code","errorClass":"RequestHandlingException","httpStatus":null,"severity":"error","filePath":"play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/RequestHandling.kt","lineNumber":176,"sourceCode":"    }\n}\n\nsuspend fun RequestOptions.checkIsValid(context: Context, origin: String, packageName: String?) {\n    val allApplicableFacetIds = hashSetOf<String>()\n    if (origin.startsWith(\"https://\")) {\n        allApplicableFacetIds.add(origin)\n        val originUri = origin.toUri()\n        // The RP ID must be equal to the origin's effective domain, or a registrable domain\n        // suffix of the origin's effective domain: For origin https://login.example.com:1337,\n        // login.example.com and example.com are valid rpId,\n        // but m.login.example.com and com aren't valid\n        // => We don't check topDomainOf(originUri.host) against topDomainOf(rpId), because:\n        // 1. rpId m.login.example.com would be a valid rpId for https://login.example.com:1337\n        // 2. it excludes internal domains as topDomainOf requires a public FQDN\n        //\n        // Instead, we first check that rpId is valid, then if it matches the origin host\n        if (runCatching { InternetDomainName.from(rpId).isPublicSuffix }.getOrDefault(false)) {\n            throw RequestHandlingException(NOT_ALLOWED_ERR, \"RP ID $rpId is a public suffix\")\n        }\n        if (\n            originUri.host != rpId &&\n            originUri.host?.endsWith(\".$rpId\") != true\n        ) {\n            throw RequestHandlingException(NOT_ALLOWED_ERR, \"RP ID $rpId not allowed from origin $origin\")\n        }\n        // FIXME: Standard suggests doing additional checks, but this is already sensible enough\n    } else if ((origin.startsWith(\"android:apk-key-hash:\") || origin.startsWith(\"android:apk-key-hash-sha256:\")) && packageName != null) {\n        allApplicableFacetIds.addAll(getAllFacetIdCandidates(context, packageName, origin))\n        val sha256facetId = allApplicableFacetIds.firstOrNull { it.startsWith(\"android:apk-key-hash-sha256:\") }\n            ?: throw RequestHandlingException(NOT_ALLOWED_ERR, \"RP ID $rpId not allowed from origin $origin\")\n        val fp = Base64.decode(sha256facetId.substring(28), HASH_BASE64_FLAGS).toHexString(\":\")\n        if (!isAssetLinked(context, rpId, fp, packageName)) {\n            throw RequestHandlingException(NOT_ALLOWED_ERR, \"RP ID $rpId not allowed from origin $origin (expected fingerprint $fp)\")\n        }\n    } else {\n        throw RequestHandlingException(NOT_SUPPORTED_ERR, \"Origin $origin not supported\")","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/microg/GmsCore/blob/157c9d86ac46c195a86c2f15ab55c84036223f95/play-services-fido/core/src/main/kotlin/org/microg/gms/fido/core/RequestHandling.kt#L158-L194","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nval options = PublicKeyCredentialCreationOptions(rpId = \"co.uk\", ...)\n\n// after\nval domain = InternetDomainName.from(\"co.uk\")\nrequire(!domain.isPublicSuffix) { \"rpId must be a registrable domain, not a public suffix\" }\nval options = PublicKeyCredentialCreationOptions(rpId = \"auth.example.co.uk\", ...)","handlingStrategy":"validation","validationCode":"import com.google.common.net.InternetDomainName\n\nfun rpIdIsRegistrable(rpId: String): Boolean = runCatching {\n    InternetDomainName.from(rpId).let { !it.isPublicSuffix }\n}.getOrDefault(false)\n\nif (!rpIdIsRegistrable(rpId)) failWith(NOT_ALLOWED_ERR, \"rpId must not be a public suffix\")","typeGuard":"fun String.isValidRpId(): Boolean = runCatching {\n    InternetDomainName.from(this).isTopPrivateDomain || InternetDomainName.from(this).isUnderPublicSuffix\n}.getOrDefault(false)","tryCatchPattern":"try {\n    fidoClient.handle(options)\n} catch (e: RequestHandlingException) {\n    if (e.code == NOT_ALLOWED_ERR) showRpIdConfigError(e.message)\n    else throw e\n}","preventionTips":["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"],"tags":["android","fido","webauthn","passkeys","validation","domain"],"backgroundTag":"invalid-argument-value","analyzedSha":"157c9d86ac46c195a86c2f15ab55c84036223f95","analyzedAt":"2026-09-06T17:27:33.892Z","contentChangedAt":"2026-09-06T17:27:33.892Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}