feat(jans-fido2): Jans fido2 attestation rejection diagnostics - #14659
feat(jans-fido2): Jans fido2 attestation rejection diagnostics#14659imran-ishaq wants to merge 7 commits into
Conversation
Add GET /jans-fido2/restv1/trust/attestation/config, a read-only view of the attestation policy the server is applying: effective mode, whether unattested authenticators are still accepted, enterprise attestation, whether the metadata service is disabled, and whether the Apple WebAuthn root CA was loaded. unattestedAuthenticatorsAllowed is derived as mode != enforced, not mode == disabled: AttestationCertificateService.isAttestationEnforced() applies the stricter MDS trust rules only for enforced, so the default monitor mode still accepts an authenticator that fails attestation. The configured mode is reported verbatim alongside a flag for whether it is recognised, since an unrecognised value silently leaves the server lenient. Mirrored through the Config API fido2 plugin for the Admin UI. Also strips trailing tab characters on two pre-existing lines of jansFido2Swagger.yaml that made the spec unparseable by strict YAML loaders. Signed-off-by: imran <imranishaq7071@gmail.com>
…do2-plugin The plugin imports io.jans.configapi.util.ApiAccessConstants and ApiConstants directly but relied on them arriving transitively through jans-config-api-server. Declare the dependency it actually uses. Signed-off-by: imran <imranishaq7071@gmail.com>
Adds a Trust Diagnostics page covering the attestation-mode endpoint, the three attestation modes, and the fact that only "enforced" rejects a failing attestation - the default "monitor" mode does not. Signed-off-by: imran <imranishaq7071@gmail.com>
Add GET /jans-fido2/restv1/trust/mds/health, reporting blob validity, loaded TOC entry count, the outcome of the last refresh, and the configured metadata servers. A stale or failed MDS load is a common cause of a previously valid authenticator being rejected, and was previously visible only in the server log. TocService now retains the last successful refresh timestamp and the last refresh error, which were caught and logged then discarded, and exposes the loaded entry count and the in-memory blob nextUpdate. The existing getNextUpdateDate() reads the document store and throws on failure, so it is not used on the request path. DOWN returns 503 so the endpoint can be wired to a monitor; UP and DISABLED return 200, since a deliberately switched-off metadata service is a configuration choice rather than an outage. MetadataServer.rootCert is reported as a presence boolean only; the certificate is never returned. Mirrored through the Config API fido2 plugin for the Admin UI. Signed-off-by: imran <imranishaq7071@gmail.com>
Extends the Trust Diagnostics page with the MDS health endpoint, the UP/DISABLED/DOWN status semantics, and the stale-metadata troubleshooting path. Signed-off-by: imran <imranishaq7071@gmail.com>
… codes
Attestation rejections were recorded as the raw exception message, so an
unknown AAGUID, an untrusted root, and an unacceptable authenticator
status were indistinguishable in metrics. Introduce internal diagnostic
codes for trust and metadata failures, written into the existing
Fido2MetricsEntry errorReason field with errorCategory ATTESTATION_TRUST.
Additive - no new metrics store.
The codes map to the messages the failure sites actually raise
(MdsService, CertificateVerifier), and each enum constant carries those
markers so the mapping stays traceable. A failure that is not trust
related keeps its original message unchanged, and the original message is
still logged when a code is substituted.
categorizeError() checks the code prefix before its keyword matching,
which would otherwise mis-bucket codes containing a keyword.
Add GET /jans-fido2/restv1/metrics/analytics/attestation-rejections,
alongside the existing /analytics/errors, returning counts per reason
code plus the registration attempts they are measured against. Mirrored
through the Config API fido2 plugin.
The public FIDO response envelope is unchanged: ErrorResponseFactory
still returns {status:"failed", errorMessage:"..."} and the codes reach
metrics and structured logs only, never the client body.
Signed-off-by: imran <imranishaq7071@gmail.com>
Adds the rejection-diagnostics endpoint and a code-to-cause table to the Trust Diagnostics page, and cross-links it from the passkey telemetry page's analytics reference. Signed-off-by: imran <imranishaq7071@gmail.com>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Prepare
Description
Target issue
The status endpoints tell an administrator what the trust configuration is, but not why individual registrations are being rejected. There is no categorization of attestation rejection causes: an unknown AAGUID, expired metadata, an attestation format not permitted by the mode, and an untrusted root certificate are indistinguishable from each other and from any other registration failure. The metrics subsystem already has the fields for this — Fido2MetricsEntry carries errorReason and errorCategory — but nothing populates them with trust-specific values, and there is no endpoint to aggregate them.
closes #14641
Implementation Details
This is the only one of the three PRs that touches attestation code paths. No rejection behaviour changes; only the reason gets recorded.
Rejections were previously recorded as the raw exception message, so an unknown AAGUID, an untrusted root, and an unacceptable authenticator status were indistinguishable in metrics.
AttestationTrustDiagnosticintroduces internalcodes written into the existing
errorReasonfield witherrorCategoryset toATTESTATION_TRUST— additive, no new store.The codes map to messages the failure sites actually raise (
MdsService,CertificateVerifier), and each enum constant carries those markers in its definition so the mapping stays traceable to the code that raises it. A failure that is not trust related keeps its original message, and the original message is still logged whenever a code is substituted, so no detail is lost.categorizeError()checks theJFS_prefix before its existing keyword matching — otherwise a code containing a keyword would be mis-bucketed (e.g.JFS_MDS_METADATA_EXPIREDreads as "expired" →TIMEOUT).GET /metrics/analytics/attestation-rejectionssits alongside/analytics/errorsand reads the same store.Two deviations from the issue as filed, both deliberate:
topAaguids.Fido2MetricsEntryhas no AAGUID field, so it cannot be computed without a persisted-modelchange. The response returns
registrationAttemptsinstead, which is what makesrejectionRateinterpretable. Adding anAAGUID dimension is a reasonable follow-up.
JFS_MDS_METADATA_EXPIREDandJFS_APPLE_ROOT_CA_MISSINGhave no emitting site in the currentcode — nothing rejects on blob expiry during attestation, and the Apple processor fails on a missing
x5crather than amissing root CA. Shipping codes that can never fire would be worse than omitting them; they can be added when a site
exists.
The public FIDO response envelope is unchanged:
ErrorResponseFactorystill returns{status:"failed", errorMessage:"…"}and codes reach metrics and structured logs only, never the client body.Test and Document the changes
Please check the below before submitting your PR. The PR will not be merged if there are no commits that start with
docs:to indicate documentation changes or if the below checklist is not selected.