File size: 6,652 Bytes
afa0cbf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | //! Experimental user-verification APIs for trusted UI clients.
//! Native implementation and registration transport are independent of these contracts.
use crate::JsonSchema;
use crate::RequestId;
use crate::TS;
use serde::Deserialize;
use serde::Serialize;
/// A signature over the exact decoded challenge. The verifier validates and consumes it.
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[ts(export_to = "v2/")]
pub struct UserVerificationProof {
pub credential_id: String,
/// Unpadded base64url DER ECDSA signature using P-256 and SHA-256.
pub signature: String,
}
impl std::fmt::Debug for UserVerificationProof {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("UserVerificationProof")
.finish_non_exhaustive()
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase", export_to = "v2/")]
pub enum UserVerificationUnavailableReason {
CredentialMissing,
BiometricsUnavailable,
ProviderUnavailable,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase", export_to = "v2/")]
pub enum UserVerificationCancellationReason {
UserCancelled,
Interrupted,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase", export_to = "v2/")]
pub enum UserVerificationFailureReason {
AuthenticationFailed,
Timeout,
ProviderError,
ServiceError,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase", export_to = "v2/")]
pub enum UserVerificationInvalidRequestReason {
InvalidParams,
}
/// Closed error categories; native diagnostic payloads must not cross this boundary.
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)]
#[ts(tag = "type", rename_all = "camelCase", export_to = "v2/")]
pub enum UserVerificationErrorDetails {
InvalidRequest {
reason: UserVerificationInvalidRequestReason,
},
Unavailable {
reason: UserVerificationUnavailableReason,
},
Cancelled {
reason: UserVerificationCancellationReason,
},
Failed {
reason: UserVerificationFailureReason,
},
}
/// The error object inside the normal JSON-RPC envelope.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[ts(export_to = "v2/")]
pub struct UserVerificationRpcError {
#[ts(type = "number")]
pub code: i64,
pub message: String,
pub data: UserVerificationErrorDetails,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[ts(export_to = "v2/")]
pub struct UserVerificationStatusParams {}
/// Local readiness only; this neither prompts nor queries server registration.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct UserVerificationStatusResponse {
pub credential_id: Option<String>,
pub unavailable_reason: Option<UserVerificationUnavailableReason>,
pub unavailable_message: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[ts(export_to = "v2/")]
pub struct UserVerificationEnrollParams {}
/// Public metadata for a created or reused local credential. The caller completes
/// backend registration; this response does not establish server enrollment.
/// Older app-servers omit the metadata fields; callers must check both before registration.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct UserVerificationEnrollResponse {
pub credential_id: String,
// TODO: Make both metadata fields required after the experimental/alpha rollout
// guarantees them on the oldest supported app-server version.
pub algorithm: Option<String>,
/// Unpadded base64url of the SubjectPublicKeyInfo DER encoding.
pub public_key: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[ts(export_to = "v2/")]
pub struct UserVerificationDeleteParams {}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct UserVerificationDeleteResponse {}
/// Local signing primitive, independent of any pending elicitation.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[ts(export_to = "v2/")]
pub struct UserVerificationVerifyParams {
/// Unpadded base64url encoding of 1–4096 challenge bytes.
pub challenge: String,
/// Display context already approved by the UI; 1–256 UTF-8 bytes.
pub title: String,
/// Additional display context; at most 4096 UTF-8 bytes.
pub description: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct UserVerificationVerifyResponse {
pub proof: UserVerificationProof,
}
/// Cancels a native verification RPC issued on this connection, not an elicitation.
/// Use a fresh request ID for each operation and a distinct ID for this cancellation RPC.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[ts(export_to = "v2/")]
pub struct UserVerificationCancelParams {
pub request_id: RequestId,
}
/// Acknowledges the cancellation signal; native work may still be finishing.
/// Unknown or finished requests are a no-op, and completed effects are not rolled back.
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct UserVerificationCancelResponse {}
#[cfg(test)]
#[path = "user_verification_tests.rs"]
mod tests;
|