//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `auth/passkey/revoke/start`. Version: `0.1`.
#[allow(unused_imports)]
use serde::{Deserialize, Serialize};
/// Error types.
pub mod error {
/// Error from a `TryFrom` or `FromStr` implementation.
pub struct ConversionError(::std::borrow::Cow<'static, str>);
impl ::std::error::Error for ConversionError {}
impl ::std::fmt::Display for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Display::fmt(&self.0, f)
}
}
impl ::std::fmt::Debug for ConversionError {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> Result<(), ::std::fmt::Error> {
::std::fmt::Debug::fmt(&self.0, f)
}
}
impl From<&'static str> for ConversionError {
fn from(value: &'static str) -> Self {
Self(value.into())
}
}
impl From<String> for ConversionError {
fn from(value: String) -> Self {
Self(value.into())
}
}
}
///`CredentialDescriptor`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "PublicKeyCredentialDescriptor",
/// "type": "object",
/// "required": [
/// "id",
/// "type"
/// ],
/// "properties": {
/// "id": {
/// "description": "base64url-encoded credential id.",
/// "type": "string"
/// },
/// "transports": {
/// "type": "array",
/// "items": {
/// "enum": [
/// "usb",
/// "nfc",
/// "ble",
/// "internal",
/// "hybrid"
/// ]
/// }
/// },
/// "type": {
/// "const": "public-key"
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "credentialDescriptor"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct CredentialDescriptor {
///base64url-encoded credential id.
pub id: ::std::string::String,
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub transports: ::std::vec::Vec<CredentialDescriptorTransportsItem>,
#[serde(rename = "type")]
pub type_: ::serde_json::Value,
}
impl CredentialDescriptor {
pub fn builder() -> builder::CredentialDescriptor {
Default::default()
}
}
///`CredentialDescriptorTransportsItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "enum": [
/// "usb",
/// "nfc",
/// "ble",
/// "internal",
/// "hybrid"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum CredentialDescriptorTransportsItem {
#[serde(rename = "usb")]
Usb,
#[serde(rename = "nfc")]
Nfc,
#[serde(rename = "ble")]
Ble,
#[serde(rename = "internal")]
Internal,
#[serde(rename = "hybrid")]
Hybrid,
}
impl ::std::fmt::Display for CredentialDescriptorTransportsItem {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Usb => f.write_str("usb"),
Self::Nfc => f.write_str("nfc"),
Self::Ble => f.write_str("ble"),
Self::Internal => f.write_str("internal"),
Self::Hybrid => f.write_str("hybrid"),
}
}
}
impl ::std::str::FromStr for CredentialDescriptorTransportsItem {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"usb" => Ok(Self::Usb),
"nfc" => Ok(Self::Nfc),
"ble" => Ok(Self::Ble),
"internal" => Ok(Self::Internal),
"hybrid" => Ok(Self::Hybrid),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for CredentialDescriptorTransportsItem {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for CredentialDescriptorTransportsItem {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for CredentialDescriptorTransportsItem {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Server-issued options for `navigator.credentials.get({ publicKey: ... })`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "PublicKeyCredentialRequestOptions",
/// "description": "Server-issued options for `navigator.credentials.get({ publicKey: ... })`.",
/// "type": "object",
/// "required": [
/// "challenge"
/// ],
/// "properties": {
/// "allowCredentials": {
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/CredentialDescriptor"
/// }
/// },
/// "challenge": {
/// "description": "base64url-encoded one-time nonce.",
/// "type": "string"
/// },
/// "extensions": {
/// "description": "\nClient extension inputs, per the WebAuthn Level 2 `AuthenticationExtensionsClientInputs` dictionary.\n\nThis component states that it mirrors the W3C dictionary, and that dictionary defines `extensions`. Omitting it while closing the object with `additionalProperties: false` made the two claims contradict each other: a server emitting standard WebAuthn options could not conform, and the widely-used server libraries emit this member by default.\n\nStructure is deliberately unconstrained. The set of extensions is open and registered outside this framework, so enumerating them here would date the schema against a registry it does not own — and a closed list would reproduce the original defect one revision later.",
/// "type": "object"
/// },
/// "rpId": {
/// "type": "string",
/// "minLength": 1
/// },
/// "timeout": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "userVerification": {
/// "enum": [
/// "discouraged",
/// "preferred",
/// "required"
/// ]
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "credentialRequestOptions"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct CredentialRequestOptions {
#[serde(
rename = "allowCredentials",
default,
skip_serializing_if = "::std::vec::Vec::is_empty"
)]
pub allow_credentials: ::std::vec::Vec<CredentialDescriptor>,
///base64url-encoded one-time nonce.
pub challenge: ::std::string::String,
/**
Client extension inputs, per the WebAuthn Level 2 `AuthenticationExtensionsClientInputs` dictionary.
This component states that it mirrors the W3C dictionary, and that dictionary defines `extensions`. Omitting it while closing the object with `additionalProperties: false` made the two claims contradict each other: a server emitting standard WebAuthn options could not conform, and the widely-used server libraries emit this member by default.
Structure is deliberately unconstrained. The set of extensions is open and registered outside this framework, so enumerating them here would date the schema against a registry it does not own — and a closed list would reproduce the original defect one revision later.*/
#[serde(default, skip_serializing_if = "::serde_json::Map::is_empty")]
pub extensions: ::serde_json::Map<::std::string::String, ::serde_json::Value>,
#[serde(
rename = "rpId",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub rp_id: ::std::option::Option<CredentialRequestOptionsRpId>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub timeout: ::std::option::Option<::std::num::NonZeroU64>,
#[serde(
rename = "userVerification",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub user_verification: ::std::option::Option<CredentialRequestOptionsUserVerification>,
}
impl CredentialRequestOptions {
pub fn builder() -> builder::CredentialRequestOptions {
Default::default()
}
}
///`CredentialRequestOptionsRpId`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct CredentialRequestOptionsRpId(::std::string::String);
impl ::std::ops::Deref for CredentialRequestOptionsRpId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<CredentialRequestOptionsRpId> for ::std::string::String {
fn from(value: CredentialRequestOptionsRpId) -> Self {
value.0
}
}
impl ::std::str::FromStr for CredentialRequestOptionsRpId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for CredentialRequestOptionsRpId {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for CredentialRequestOptionsRpId {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for CredentialRequestOptionsRpId {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for CredentialRequestOptionsRpId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`CredentialRequestOptionsUserVerification`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "enum": [
/// "discouraged",
/// "preferred",
/// "required"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
#[non_exhaustive]
pub enum CredentialRequestOptionsUserVerification {
#[serde(rename = "discouraged")]
Discouraged,
#[serde(rename = "preferred")]
Preferred,
#[serde(rename = "required")]
Required,
}
impl ::std::fmt::Display for CredentialRequestOptionsUserVerification {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Discouraged => f.write_str("discouraged"),
Self::Preferred => f.write_str("preferred"),
Self::Required => f.write_str("required"),
}
}
}
impl ::std::str::FromStr for CredentialRequestOptionsUserVerification {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"discouraged" => Ok(Self::Discouraged),
"preferred" => Ok(Self::Preferred),
"required" => Ok(Self::Required),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for CredentialRequestOptionsUserVerification {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for CredentialRequestOptionsUserVerification {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for CredentialRequestOptionsUserVerification {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Ext",
/// "description": "Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.",
/// "type": "object",
/// "minProperties": 1,
/// "additionalProperties": true,
/// "propertyNames": {
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(transparent)]
pub struct Ext(pub ::std::collections::HashMap<ExtKey, ::serde_json::Value>);
impl ::std::ops::Deref for Ext {
type Target = ::std::collections::HashMap<ExtKey, ::serde_json::Value>;
fn deref(&self) -> &::std::collections::HashMap<ExtKey, ::serde_json::Value> {
&self.0
}
}
impl ::std::convert::From<Ext> for ::std::collections::HashMap<ExtKey, ::serde_json::Value> {
fn from(value: Ext) -> Self {
value.0
}
}
impl ::std::convert::From<::std::collections::HashMap<ExtKey, ::serde_json::Value>> for Ext {
fn from(value: ::std::collections::HashMap<ExtKey, ::serde_json::Value>) -> Self {
Self(value)
}
}
///`ExtKey`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ExtKey(::std::string::String);
impl ::std::ops::Deref for ExtKey {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ExtKey> for ::std::string::String {
fn from(value: ExtKey) -> Self {
value.0
}
}
impl ::std::str::FromStr for ExtKey {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> =
::std::sync::LazyLock::new(|| {
::regress::Regex::new("^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$").unwrap()
});
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ExtKey {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ExtKey {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Ask the auth service to begin revoking one of the subject's passkeys. The response carries PublicKeyCredentialRequestOptions for a fresh user-verification ceremony; nothing is removed until the matching auth/passkey/revoke/finish presents the assertion.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/auth/passkey/revoke/start/0.1",
/// "title": "Payload",
/// "description": "Ask the auth service to begin revoking one of the subject's passkeys. The response carries PublicKeyCredentialRequestOptions for a fresh user-verification ceremony; nothing is removed until the matching auth/passkey/revoke/finish presents the assertion.",
/// "type": "object",
/// "required": [
/// "credentialId"
/// ],
/// "properties": {
/// "credentialId": {
/// "description": "The credential to revoke, as returned by auth/passkey/list. Echoed verbatim; opaque to the producer.",
/// "type": "string",
/// "minLength": 1
/// },
/// "ext": {
/// "description": "Ecosystem-defined extension members per SPEC.md §4.5.1.",
/// "$ref": "#/definitions/Ext"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Payload {
///The credential to revoke, as returned by auth/passkey/list. Echoed verbatim; opaque to the producer.
#[serde(rename = "credentialId")]
pub credential_id: PayloadCredentialId,
///Ecosystem-defined extension members per SPEC.md §4.5.1.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
}
impl Payload {
pub fn builder() -> builder::Payload {
Default::default()
}
}
///The credential to revoke, as returned by auth/passkey/list. Echoed verbatim; opaque to the producer.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The credential to revoke, as returned by auth/passkey/list. Echoed verbatim; opaque to the producer.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PayloadCredentialId(::std::string::String);
impl ::std::ops::Deref for PayloadCredentialId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PayloadCredentialId> for ::std::string::String {
fn from(value: PayloadCredentialId) -> Self {
value.0
}
}
impl ::std::str::FromStr for PayloadCredentialId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PayloadCredentialId {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PayloadCredentialId {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PayloadCredentialId {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for PayloadCredentialId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Server-issued re-authentication options. Carried in a Trust Task document whose type is https://trusttasks.org/spec/auth/passkey/revoke/start/0.1#response.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "description": "Server-issued re-authentication options. Carried in a Trust Task document whose type is https://trusttasks.org/spec/auth/passkey/revoke/start/0.1#response.",
/// "type": "object",
/// "required": [
/// "revocationId",
/// "uvOptions"
/// ],
/// "properties": {
/// "ext": {
/// "description": "Ecosystem-defined extension members per SPEC.md §4.5.1.",
/// "$ref": "#/definitions/Ext"
/// },
/// "revocationId": {
/// "description": "Opaque server handle correlating this start with the matching finish. The producer MUST echo it verbatim. The consumer binds it to the target credentialId server-side, so the finish carries no target of its own.",
/// "type": "string",
/// "minLength": 1
/// },
/// "uvOptions": {
/// "description": "PublicKeyCredentialRequestOptions for navigator.credentials.get — a fresh user-verification ceremony proving a human with an enrolled authenticator is present right now. userVerification SHOULD be \"required\".",
/// "$ref": "#/definitions/CredentialRequestOptions"
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
#[non_exhaustive]
pub struct Response {
///Ecosystem-defined extension members per SPEC.md §4.5.1.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ext: ::std::option::Option<Ext>,
///Opaque server handle correlating this start with the matching finish. The producer MUST echo it verbatim. The consumer binds it to the target credentialId server-side, so the finish carries no target of its own.
#[serde(rename = "revocationId")]
pub revocation_id: ResponseRevocationId,
///PublicKeyCredentialRequestOptions for navigator.credentials.get — a fresh user-verification ceremony proving a human with an enrolled authenticator is present right now. userVerification SHOULD be "required".
#[serde(rename = "uvOptions")]
pub uv_options: CredentialRequestOptions,
}
impl Response {
pub fn builder() -> builder::Response {
Default::default()
}
}
///Opaque server handle correlating this start with the matching finish. The producer MUST echo it verbatim. The consumer binds it to the target credentialId server-side, so the finish carries no target of its own.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Opaque server handle correlating this start with the matching finish. The producer MUST echo it verbatim. The consumer binds it to the target credentialId server-side, so the finish carries no target of its own.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ResponseRevocationId(::std::string::String);
impl ::std::ops::Deref for ResponseRevocationId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ResponseRevocationId> for ::std::string::String {
fn from(value: ResponseRevocationId) -> Self {
value.0
}
}
impl ::std::str::FromStr for ResponseRevocationId {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ResponseRevocationId {
type Error = self::error::ConversionError;
fn try_from(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ResponseRevocationId {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ResponseRevocationId {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ResponseRevocationId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
/// Types for composing complex structures.
pub mod builder {
#[derive(Clone, Debug)]
pub struct CredentialDescriptor {
id: ::std::result::Result<::std::string::String, ::std::string::String>,
transports: ::std::result::Result<
::std::vec::Vec<super::CredentialDescriptorTransportsItem>,
::std::string::String,
>,
type_: ::std::result::Result<::serde_json::Value, ::std::string::String>,
}
impl ::std::default::Default for CredentialDescriptor {
fn default() -> Self {
Self {
id: Err("no value supplied for id".to_string()),
transports: Ok(Default::default()),
type_: Err("no value supplied for type_".to_string()),
}
}
}
impl CredentialDescriptor {
pub fn id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::string::String>,
T::Error: ::std::fmt::Display,
{
self.id = value
.try_into()
.map_err(|e| format!("error converting supplied value for id: {e}"));
self
}
pub fn transports<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::CredentialDescriptorTransportsItem>>,
T::Error: ::std::fmt::Display,
{
self.transports = value
.try_into()
.map_err(|e| format!("error converting supplied value for transports: {e}"));
self
}
pub fn type_<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::serde_json::Value>,
T::Error: ::std::fmt::Display,
{
self.type_ = value
.try_into()
.map_err(|e| format!("error converting supplied value for type_: {e}"));
self
}
}
impl ::std::convert::TryFrom<CredentialDescriptor> for super::CredentialDescriptor {
type Error = super::error::ConversionError;
fn try_from(
value: CredentialDescriptor,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
id: value.id?,
transports: value.transports?,
type_: value.type_?,
})
}
}
impl ::std::convert::From<super::CredentialDescriptor> for CredentialDescriptor {
fn from(value: super::CredentialDescriptor) -> Self {
Self {
id: Ok(value.id),
transports: Ok(value.transports),
type_: Ok(value.type_),
}
}
}
#[derive(Clone, Debug)]
pub struct CredentialRequestOptions {
allow_credentials: ::std::result::Result<
::std::vec::Vec<super::CredentialDescriptor>,
::std::string::String,
>,
challenge: ::std::result::Result<::std::string::String, ::std::string::String>,
extensions: ::std::result::Result<
::serde_json::Map<::std::string::String, ::serde_json::Value>,
::std::string::String,
>,
rp_id: ::std::result::Result<
::std::option::Option<super::CredentialRequestOptionsRpId>,
::std::string::String,
>,
timeout: ::std::result::Result<
::std::option::Option<::std::num::NonZeroU64>,
::std::string::String,
>,
user_verification: ::std::result::Result<
::std::option::Option<super::CredentialRequestOptionsUserVerification>,
::std::string::String,
>,
}
impl ::std::default::Default for CredentialRequestOptions {
fn default() -> Self {
Self {
allow_credentials: Ok(Default::default()),
challenge: Err("no value supplied for challenge".to_string()),
extensions: Ok(Default::default()),
rp_id: Ok(Default::default()),
timeout: Ok(Default::default()),
user_verification: Ok(Default::default()),
}
}
}
impl CredentialRequestOptions {
pub fn allow_credentials<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::vec::Vec<super::CredentialDescriptor>>,
T::Error: ::std::fmt::Display,
{
self.allow_credentials = value
.try_into()
.map_err(|e| format!("error converting supplied value for allow_credentials: {e}"));
self
}
pub fn challenge<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::string::String>,
T::Error: ::std::fmt::Display,
{
self.challenge = value
.try_into()
.map_err(|e| format!("error converting supplied value for challenge: {e}"));
self
}
pub fn extensions<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::serde_json::Map<::std::string::String, ::serde_json::Value>,
>,
T::Error: ::std::fmt::Display,
{
self.extensions = value
.try_into()
.map_err(|e| format!("error converting supplied value for extensions: {e}"));
self
}
pub fn rp_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::CredentialRequestOptionsRpId>>,
T::Error: ::std::fmt::Display,
{
self.rp_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for rp_id: {e}"));
self
}
pub fn timeout<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<::std::num::NonZeroU64>>,
T::Error: ::std::fmt::Display,
{
self.timeout = value
.try_into()
.map_err(|e| format!("error converting supplied value for timeout: {e}"));
self
}
pub fn user_verification<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<
::std::option::Option<super::CredentialRequestOptionsUserVerification>,
>,
T::Error: ::std::fmt::Display,
{
self.user_verification = value
.try_into()
.map_err(|e| format!("error converting supplied value for user_verification: {e}"));
self
}
}
impl ::std::convert::TryFrom<CredentialRequestOptions> for super::CredentialRequestOptions {
type Error = super::error::ConversionError;
fn try_from(
value: CredentialRequestOptions,
) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
allow_credentials: value.allow_credentials?,
challenge: value.challenge?,
extensions: value.extensions?,
rp_id: value.rp_id?,
timeout: value.timeout?,
user_verification: value.user_verification?,
})
}
}
impl ::std::convert::From<super::CredentialRequestOptions> for CredentialRequestOptions {
fn from(value: super::CredentialRequestOptions) -> Self {
Self {
allow_credentials: Ok(value.allow_credentials),
challenge: Ok(value.challenge),
extensions: Ok(value.extensions),
rp_id: Ok(value.rp_id),
timeout: Ok(value.timeout),
user_verification: Ok(value.user_verification),
}
}
}
#[derive(Clone, Debug)]
pub struct Payload {
credential_id: ::std::result::Result<super::PayloadCredentialId, ::std::string::String>,
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
}
impl ::std::default::Default for Payload {
fn default() -> Self {
Self {
credential_id: Err("no value supplied for credential_id".to_string()),
ext: Ok(Default::default()),
}
}
}
impl Payload {
pub fn credential_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::PayloadCredentialId>,
T::Error: ::std::fmt::Display,
{
self.credential_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for credential_id: {e}"));
self
}
pub fn ext<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Ext>>,
T::Error: ::std::fmt::Display,
{
self.ext = value
.try_into()
.map_err(|e| format!("error converting supplied value for ext: {e}"));
self
}
}
impl ::std::convert::TryFrom<Payload> for super::Payload {
type Error = super::error::ConversionError;
fn try_from(value: Payload) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
credential_id: value.credential_id?,
ext: value.ext?,
})
}
}
impl ::std::convert::From<super::Payload> for Payload {
fn from(value: super::Payload) -> Self {
Self {
credential_id: Ok(value.credential_id),
ext: Ok(value.ext),
}
}
}
#[derive(Clone, Debug)]
pub struct Response {
ext: ::std::result::Result<::std::option::Option<super::Ext>, ::std::string::String>,
revocation_id: ::std::result::Result<super::ResponseRevocationId, ::std::string::String>,
uv_options: ::std::result::Result<super::CredentialRequestOptions, ::std::string::String>,
}
impl ::std::default::Default for Response {
fn default() -> Self {
Self {
ext: Ok(Default::default()),
revocation_id: Err("no value supplied for revocation_id".to_string()),
uv_options: Err("no value supplied for uv_options".to_string()),
}
}
}
impl Response {
pub fn ext<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<::std::option::Option<super::Ext>>,
T::Error: ::std::fmt::Display,
{
self.ext = value
.try_into()
.map_err(|e| format!("error converting supplied value for ext: {e}"));
self
}
pub fn revocation_id<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::ResponseRevocationId>,
T::Error: ::std::fmt::Display,
{
self.revocation_id = value
.try_into()
.map_err(|e| format!("error converting supplied value for revocation_id: {e}"));
self
}
pub fn uv_options<T>(mut self, value: T) -> Self
where
T: ::std::convert::TryInto<super::CredentialRequestOptions>,
T::Error: ::std::fmt::Display,
{
self.uv_options = value
.try_into()
.map_err(|e| format!("error converting supplied value for uv_options: {e}"));
self
}
}
impl ::std::convert::TryFrom<Response> for super::Response {
type Error = super::error::ConversionError;
fn try_from(value: Response) -> ::std::result::Result<Self, super::error::ConversionError> {
Ok(Self {
ext: value.ext?,
revocation_id: value.revocation_id?,
uv_options: value.uv_options?,
})
}
}
impl ::std::convert::From<super::Response> for Response {
fn from(value: super::Response) -> Self {
Self {
ext: Ok(value.ext),
revocation_id: Ok(value.revocation_id),
uv_options: Ok(value.uv_options),
}
}
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/auth/passkey/revoke/start/0.1";
const IS_PROOF_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"CredentialDescriptor\": {\n \"$anchor\": \"credentialDescriptor\",\n \"additionalProperties\": false,\n \"properties\": {\n \"id\": {\n \"description\": \"base64url-encoded credential id.\",\n \"type\": \"string\"\n },\n \"transports\": {\n \"items\": {\n \"enum\": [\n \"usb\",\n \"nfc\",\n \"ble\",\n \"internal\",\n \"hybrid\"\n ]\n },\n \"type\": \"array\"\n },\n \"type\": {\n \"const\": \"public-key\"\n }\n },\n \"required\": [\n \"type\",\n \"id\"\n ],\n \"title\": \"PublicKeyCredentialDescriptor\",\n \"type\": \"object\"\n },\n \"CredentialRequestOptions\": {\n \"$anchor\": \"credentialRequestOptions\",\n \"additionalProperties\": false,\n \"description\": \"Server-issued options for `navigator.credentials.get({ publicKey: ... })`.\",\n \"properties\": {\n \"allowCredentials\": {\n \"items\": {\n \"$ref\": \"#/$defs/CredentialDescriptor\"\n },\n \"type\": \"array\"\n },\n \"challenge\": {\n \"description\": \"base64url-encoded one-time nonce.\",\n \"type\": \"string\"\n },\n \"extensions\": {\n \"description\": \"Client extension inputs, per the WebAuthn Level 2 `AuthenticationExtensionsClientInputs` dictionary.\\n\\nThis component states that it mirrors the W3C dictionary, and that dictionary defines `extensions`. Omitting it while closing the object with `additionalProperties: false` made the two claims contradict each other: a server emitting standard WebAuthn options could not conform, and the widely-used server libraries emit this member by default.\\n\\nStructure is deliberately unconstrained. The set of extensions is open and registered outside this framework, so enumerating them here would date the schema against a registry it does not own — and a closed list would reproduce the original defect one revision later.\",\n \"type\": \"object\"\n },\n \"rpId\": {\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"timeout\": {\n \"minimum\": 1,\n \"type\": \"integer\"\n },\n \"userVerification\": {\n \"enum\": [\n \"discouraged\",\n \"preferred\",\n \"required\"\n ]\n }\n },\n \"required\": [\n \"challenge\"\n ],\n \"title\": \"PublicKeyCredentialRequestOptions\",\n \"type\": \"object\"\n },\n \"Ext\": {\n \"additionalProperties\": true,\n \"description\": \"Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.\",\n \"minProperties\": 1,\n \"propertyNames\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"\n },\n \"title\": \"Ext\",\n \"type\": \"object\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"Server-issued re-authentication options. Carried in a Trust Task document whose type is https://trusttasks.org/spec/auth/passkey/revoke/start/0.1#response.\",\n \"properties\": {\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1.\"\n },\n \"revocationId\": {\n \"description\": \"Opaque server handle correlating this start with the matching finish. The producer MUST echo it verbatim. The consumer binds it to the target credentialId server-side, so the finish carries no target of its own.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"uvOptions\": {\n \"$ref\": \"#/$defs/CredentialRequestOptions\",\n \"description\": \"PublicKeyCredentialRequestOptions for navigator.credentials.get — a fresh user-verification ceremony proving a human with an enrolled authenticator is present right now. userVerification SHOULD be \\\"required\\\".\"\n }\n },\n \"required\": [\n \"revocationId\",\n \"uvOptions\"\n ],\n \"title\": \"Auth Passkey Revoke Start — response payload\",\n \"type\": \"object\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/auth/passkey/revoke/start/0.1\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"description\": \"Ask the auth service to begin revoking one of the subject's passkeys. The response carries PublicKeyCredentialRequestOptions for a fresh user-verification ceremony; nothing is removed until the matching auth/passkey/revoke/finish presents the assertion.\",\n \"properties\": {\n \"credentialId\": {\n \"description\": \"The credential to revoke, as returned by auth/passkey/list. Echoed verbatim; opaque to the producer.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1.\"\n }\n },\n \"required\": [\n \"credentialId\"\n ],\n \"title\": \"Auth — Passkey Revoke (start)\",\n \"type\": \"object\"\n}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str =
"https://trusttasks.org/spec/auth/passkey/revoke/start/0.1#response";
const IS_PROOF_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"CredentialDescriptor\": {\n \"$anchor\": \"credentialDescriptor\",\n \"additionalProperties\": false,\n \"properties\": {\n \"id\": {\n \"description\": \"base64url-encoded credential id.\",\n \"type\": \"string\"\n },\n \"transports\": {\n \"items\": {\n \"enum\": [\n \"usb\",\n \"nfc\",\n \"ble\",\n \"internal\",\n \"hybrid\"\n ]\n },\n \"type\": \"array\"\n },\n \"type\": {\n \"const\": \"public-key\"\n }\n },\n \"required\": [\n \"type\",\n \"id\"\n ],\n \"title\": \"PublicKeyCredentialDescriptor\",\n \"type\": \"object\"\n },\n \"CredentialRequestOptions\": {\n \"$anchor\": \"credentialRequestOptions\",\n \"additionalProperties\": false,\n \"description\": \"Server-issued options for `navigator.credentials.get({ publicKey: ... })`.\",\n \"properties\": {\n \"allowCredentials\": {\n \"items\": {\n \"$ref\": \"#/$defs/CredentialDescriptor\"\n },\n \"type\": \"array\"\n },\n \"challenge\": {\n \"description\": \"base64url-encoded one-time nonce.\",\n \"type\": \"string\"\n },\n \"extensions\": {\n \"description\": \"Client extension inputs, per the WebAuthn Level 2 `AuthenticationExtensionsClientInputs` dictionary.\\n\\nThis component states that it mirrors the W3C dictionary, and that dictionary defines `extensions`. Omitting it while closing the object with `additionalProperties: false` made the two claims contradict each other: a server emitting standard WebAuthn options could not conform, and the widely-used server libraries emit this member by default.\\n\\nStructure is deliberately unconstrained. The set of extensions is open and registered outside this framework, so enumerating them here would date the schema against a registry it does not own — and a closed list would reproduce the original defect one revision later.\",\n \"type\": \"object\"\n },\n \"rpId\": {\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"timeout\": {\n \"minimum\": 1,\n \"type\": \"integer\"\n },\n \"userVerification\": {\n \"enum\": [\n \"discouraged\",\n \"preferred\",\n \"required\"\n ]\n }\n },\n \"required\": [\n \"challenge\"\n ],\n \"title\": \"PublicKeyCredentialRequestOptions\",\n \"type\": \"object\"\n },\n \"Ext\": {\n \"additionalProperties\": true,\n \"description\": \"Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.\",\n \"minProperties\": 1,\n \"propertyNames\": {\n \"pattern\": \"^[a-z][a-z0-9-]*(\\\\.[a-z0-9-]+)+$\"\n },\n \"title\": \"Ext\",\n \"type\": \"object\"\n },\n \"Response\": {\n \"$anchor\": \"response\",\n \"additionalProperties\": false,\n \"description\": \"Server-issued re-authentication options. Carried in a Trust Task document whose type is https://trusttasks.org/spec/auth/passkey/revoke/start/0.1#response.\",\n \"properties\": {\n \"ext\": {\n \"$ref\": \"#/$defs/Ext\",\n \"description\": \"Ecosystem-defined extension members per SPEC.md §4.5.1.\"\n },\n \"revocationId\": {\n \"description\": \"Opaque server handle correlating this start with the matching finish. The producer MUST echo it verbatim. The consumer binds it to the target credentialId server-side, so the finish carries no target of its own.\",\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"uvOptions\": {\n \"$ref\": \"#/$defs/CredentialRequestOptions\",\n \"description\": \"PublicKeyCredentialRequestOptions for navigator.credentials.get — a fresh user-verification ceremony proving a human with an enrolled authenticator is present right now. userVerification SHOULD be \\\"required\\\".\"\n }\n },\n \"required\": [\n \"revocationId\",\n \"uvOptions\"\n ],\n \"title\": \"Auth Passkey Revoke Start — response payload\",\n \"type\": \"object\"\n }\n },\n \"$ref\": \"#/$defs/Response\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\n}\n",
);
}
impl crate::RequestPayload for Payload {
type Response = Response;
}
#[cfg(test)]
mod conformance {
//! Round-trip tests harvested from the spec's `spec.md`,
//! plus a `rejects_invalid_examples` test for any fixtures
//! in `payload.invalid-examples.json` (validate feature).
#[test]
fn response_example_1() {
const JSON: &str = "{\n \"id\": \"pk-rev-resp-5555-6666-7777-888888888888\",\n \"type\": \"https://trusttasks.org/spec/auth/passkey/revoke/start/0.1#response\",\n \"threadId\": \"pk-rev-1111-2222-3333-444444444444\",\n \"issuer\": \"did:web:auth.example\",\n \"recipient\": \"did:web:alice.example\",\n \"issuedAt\": \"2026-07-27T10:00:01Z\",\n \"payload\": {\n \"revocationId\": \"rev_9f8e7d6c5b4a3210\",\n \"uvOptions\": {\n \"challenge\": \"cmV2b2tlLWNoYWxsZW5nZQ\",\n \"rpId\": \"auth.example\",\n \"timeout\": 60000,\n \"userVerification\": \"required\",\n \"allowCredentials\": [\n { \"type\": \"public-key\", \"id\": \"q1w2e3r4t5y6u7i8o9p0\", \"transports\": [\"internal\", \"hybrid\"] }\n ]\n }\n }\n}\n";
let doc: crate::TrustTask<super::Response> =
serde_json::from_str(JSON).expect("deserialize response example");
let rendered = serde_json::to_value(&doc).expect("re-serialize");
let expected: serde_json::Value = serde_json::from_str(JSON).expect("re-parse expected");
assert_eq!(rendered, expected, "response example failed round-trip");
}
/// Each fixture in `payload.invalid-examples.json` MUST be
/// rejected by at least one of: serde deserialization, or
/// JSON-Schema validation under the `validate` feature. The
/// fixture file documents the producer-side bug class that
/// each payload exemplifies; this generated test pins it.
#[cfg(feature = "validate")]
#[test]
fn rejects_invalid_examples() {
use crate::validate::ValidatedPayload;
let fixtures: &[(&str, &str)] = &[
(
"Missing credentialId — the target is required at start, because the consumer binds it to the revocationId rather than accepting it at finish.",
"{}",
),
("Empty credentialId.", "{\n \"credentialId\": \"\"\n}"),
(
"Unknown top-level member. `subject` in particular is rejected: the credential owner comes from the proof, never from the payload.",
"{\n \"credentialId\": \"q1w2e3r4t5y6u7i8o9p0\",\n \"subject\": \"did:web:alice.example\"\n}",
),
(
"Array of credential ids — revocation is one credential per ceremony, so that each removal is separately user-verified.",
"{\n \"credentialId\": [\n \"q1w2e3r4t5y6u7i8o9p0\",\n \"z9x8c7v6b5n4m3k2j1h0\"\n ]\n}",
),
(
"Bare/unnamespaced ext key.",
"{\n \"credentialId\": \"q1w2e3r4t5y6u7i8o9p0\",\n \"ext\": {\n \"bare-key\": {\n \"anything\": \"here\"\n }\n }\n}",
),
];
for (i, (note, raw)) in fixtures.iter().enumerate() {
let value: serde_json::Value = match serde_json::from_str(raw) {
Ok(v) => v,
Err(_) => continue,
};
let serde_ok = serde_json::from_value::<super::Payload>(value.clone()).is_ok();
let schema_ok = super::Payload::validate_value(&value).is_ok();
assert!(
!(serde_ok && schema_ok),
"invalid-example #{} ({:?}) was accepted by both serde and JSON Schema; \
the fixture's stated failure class is no longer caught:\n{}",
i + 1,
note,
raw
);
}
}
}