Return public key metadata from user verification enrollment (#44877)

## Why

The trusted UI host needs the local credential's public metadata to complete backend registration. `userVerification/enroll` previously returned only `credentialId`.

## What changed

- Return `algorithm` and `publicKey` for newly created or reused credentials. The algorithm is `ecdsaP256Sha256X962`; the public key is unpadded base64url SPKI-DER.
- Keep both fields optional in the protocol for compatibility with older app-servers, while current servers populate both.
- Document caller-owned backend registration and revocation, including checking metadata, signing an enrollment challenge with `userVerification/verify`, matching credential IDs, and preserving the authenticated account throughout registration.

## Testing

Add protocol coverage for absent or null metadata and populated-response round trips. Add an RPC assertion for enrollment metadata and extend the local enrollment test to check metadata when creating and reusing a key.

GitOrigin-RevId: f0726e8c430e27559e1a01ba2ea635993cbeba09
This commit is contained in:
riley-oai authored and copyberry committed 2026-09-11 17:36:50 +00:00
1 parent 2c9e1a5775
commit 7b491281c8
8 files changed
+89 -9

No files matched your search

@@ -2,6 +2,35 @@ use super::*;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn enrollment_response_accepts_older_servers_without_public_metadata() {
for response in [
json!({"credentialId": "credential"}),
json!({"credentialId": "credential", "algorithm": null, "publicKey": null}),
] {
assert_eq!(
serde_json::from_value::<UserVerificationEnrollResponse>(response).unwrap(),
UserVerificationEnrollResponse {
credential_id: "credential".into(),
algorithm: None,
public_key: None,
}
);
}
let response = UserVerificationEnrollResponse {
credential_id: "credential".into(),
algorithm: Some("ecdsaP256Sha256X962".into()),
public_key: Some("public-key".into()),
};
assert_eq!(
serde_json::from_value::<UserVerificationEnrollResponse>(
serde_json::to_value(&response).unwrap()
)
.unwrap(),
response
);
}
#[test]
fn local_readiness_retains_credential_during_biometric_unavailability() {
let status = UserVerificationStatusResponse {