//! Generated by `trust-tasks-codegen` — do not edit by hand.
//!
//! Spec slug: `auth/passkey/enroll/start`. Version: `0.2`.
#[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())
}
}
}
///Server-issued options for `navigator.credentials.create({ publicKey: ... })`. Mirrors the WebAuthn Level 2 `PublicKeyCredentialCreationOptions` dictionary; binary fields are base64url-encoded strings (rather than ArrayBuffers) so the value is JSON-safe over the wire.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "PublicKeyCredentialCreationOptions",
/// "description": "Server-issued options for `navigator.credentials.create({ publicKey: ... })`. Mirrors the WebAuthn Level 2 `PublicKeyCredentialCreationOptions` dictionary; binary fields are base64url-encoded strings (rather than ArrayBuffers) so the value is JSON-safe over the wire.",
/// "type": "object",
/// "required": [
/// "challenge",
/// "pubKeyCredParams",
/// "rp",
/// "user"
/// ],
/// "properties": {
/// "attestation": {
/// "enum": [
/// "none",
/// "indirect",
/// "direct",
/// "enterprise"
/// ]
/// },
/// "authenticatorSelection": {
/// "type": "object",
/// "properties": {
/// "authenticatorAttachment": {
/// "enum": [
/// "platform",
/// "cross-platform"
/// ]
/// },
/// "requireResidentKey": {
/// "type": "boolean"
/// },
/// "residentKey": {
/// "enum": [
/// "discouraged",
/// "preferred",
/// "required"
/// ]
/// },
/// "userVerification": {
/// "enum": [
/// "discouraged",
/// "preferred",
/// "required"
/// ]
/// }
/// },
/// "additionalProperties": false
/// },
/// "challenge": {
/// "description": "base64url-encoded one-time nonce.",
/// "type": "string"
/// },
/// "excludeCredentials": {
/// "type": "array",
/// "items": {
/// "$ref": "#/definitions/CredentialDescriptor"
/// }
/// },
/// "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"
/// },
/// "pubKeyCredParams": {
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "alg",
/// "type"
/// ],
/// "properties": {
/// "alg": {
/// "description": "COSE algorithm identifier (e.g. -8 Ed25519, -7 ES256, -257 RS256).",
/// "type": "integer"
/// },
/// "type": {
/// "const": "public-key"
/// }
/// },
/// "additionalProperties": false
/// },
/// "minItems": 1
/// },
/// "rp": {
/// "type": "object",
/// "required": [
/// "id",
/// "name"
/// ],
/// "properties": {
/// "id": {
/// "type": "string",
/// "minLength": 1
/// },
/// "name": {
/// "type": "string",
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false
/// },
/// "timeout": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "user": {
/// "type": "object",
/// "required": [
/// "displayName",
/// "id",
/// "name"
/// ],
/// "properties": {
/// "displayName": {
/// "type": "string"
/// },
/// "id": {
/// "description": "base64url-encoded user handle. SHOULD be a stable opaque identifier — never reuse a human-readable username here.",
/// "type": "string"
/// },
/// "name": {
/// "type": "string",
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "credentialCreationOptions"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct CredentialCreationOptions {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub attestation: ::std::option::Option<CredentialCreationOptionsAttestation>,
#[serde(
rename = "authenticatorSelection",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub authenticator_selection:
::std::option::Option<CredentialCreationOptionsAuthenticatorSelection>,
///base64url-encoded one-time nonce.
pub challenge: ::std::string::String,
#[serde(
rename = "excludeCredentials",
default,
skip_serializing_if = "::std::vec::Vec::is_empty"
)]
pub exclude_credentials: ::std::vec::Vec<CredentialDescriptor>,
/**
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 = "pubKeyCredParams")]
pub pub_key_cred_params: ::std::vec::Vec<CredentialCreationOptionsPubKeyCredParamsItem>,
pub rp: CredentialCreationOptionsRp,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub timeout: ::std::option::Option<::std::num::NonZeroU64>,
pub user: CredentialCreationOptionsUser,
}
///`CredentialCreationOptionsAttestation`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "enum": [
/// "none",
/// "indirect",
/// "direct",
/// "enterprise"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum CredentialCreationOptionsAttestation {
#[serde(rename = "none")]
None,
#[serde(rename = "indirect")]
Indirect,
#[serde(rename = "direct")]
Direct,
#[serde(rename = "enterprise")]
Enterprise,
}
impl ::std::fmt::Display for CredentialCreationOptionsAttestation {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::None => f.write_str("none"),
Self::Indirect => f.write_str("indirect"),
Self::Direct => f.write_str("direct"),
Self::Enterprise => f.write_str("enterprise"),
}
}
}
impl ::std::str::FromStr for CredentialCreationOptionsAttestation {
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"none" => Ok(Self::None),
"indirect" => Ok(Self::Indirect),
"direct" => Ok(Self::Direct),
"enterprise" => Ok(Self::Enterprise),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for CredentialCreationOptionsAttestation {
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 CredentialCreationOptionsAttestation {
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 CredentialCreationOptionsAttestation {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`CredentialCreationOptionsAuthenticatorSelection`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "properties": {
/// "authenticatorAttachment": {
/// "enum": [
/// "platform",
/// "cross-platform"
/// ]
/// },
/// "requireResidentKey": {
/// "type": "boolean"
/// },
/// "residentKey": {
/// "enum": [
/// "discouraged",
/// "preferred",
/// "required"
/// ]
/// },
/// "userVerification": {
/// "enum": [
/// "discouraged",
/// "preferred",
/// "required"
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct CredentialCreationOptionsAuthenticatorSelection {
#[serde(
rename = "authenticatorAttachment",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub authenticator_attachment: ::std::option::Option<
CredentialCreationOptionsAuthenticatorSelectionAuthenticatorAttachment,
>,
#[serde(
rename = "requireResidentKey",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub require_resident_key: ::std::option::Option<bool>,
#[serde(
rename = "residentKey",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub resident_key:
::std::option::Option<CredentialCreationOptionsAuthenticatorSelectionResidentKey>,
#[serde(
rename = "userVerification",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub user_verification:
::std::option::Option<CredentialCreationOptionsAuthenticatorSelectionUserVerification>,
}
impl ::std::default::Default for CredentialCreationOptionsAuthenticatorSelection {
fn default() -> Self {
Self {
authenticator_attachment: Default::default(),
require_resident_key: Default::default(),
resident_key: Default::default(),
user_verification: Default::default(),
}
}
}
///`CredentialCreationOptionsAuthenticatorSelectionAuthenticatorAttachment`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "enum": [
/// "platform",
/// "cross-platform"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum CredentialCreationOptionsAuthenticatorSelectionAuthenticatorAttachment {
#[serde(rename = "platform")]
Platform,
#[serde(rename = "cross-platform")]
CrossPlatform,
}
impl ::std::fmt::Display
for CredentialCreationOptionsAuthenticatorSelectionAuthenticatorAttachment
{
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Platform => f.write_str("platform"),
Self::CrossPlatform => f.write_str("cross-platform"),
}
}
}
impl ::std::str::FromStr
for CredentialCreationOptionsAuthenticatorSelectionAuthenticatorAttachment
{
type Err = self::error::ConversionError;
fn from_str(value: &str) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"platform" => Ok(Self::Platform),
"cross-platform" => Ok(Self::CrossPlatform),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str>
for CredentialCreationOptionsAuthenticatorSelectionAuthenticatorAttachment
{
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 CredentialCreationOptionsAuthenticatorSelectionAuthenticatorAttachment
{
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 CredentialCreationOptionsAuthenticatorSelectionAuthenticatorAttachment
{
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`CredentialCreationOptionsAuthenticatorSelectionResidentKey`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "enum": [
/// "discouraged",
/// "preferred",
/// "required"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum CredentialCreationOptionsAuthenticatorSelectionResidentKey {
#[serde(rename = "discouraged")]
Discouraged,
#[serde(rename = "preferred")]
Preferred,
#[serde(rename = "required")]
Required,
}
impl ::std::fmt::Display for CredentialCreationOptionsAuthenticatorSelectionResidentKey {
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 CredentialCreationOptionsAuthenticatorSelectionResidentKey {
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 CredentialCreationOptionsAuthenticatorSelectionResidentKey {
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 CredentialCreationOptionsAuthenticatorSelectionResidentKey
{
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 CredentialCreationOptionsAuthenticatorSelectionResidentKey
{
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`CredentialCreationOptionsAuthenticatorSelectionUserVerification`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "enum": [
/// "discouraged",
/// "preferred",
/// "required"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
)]
pub enum CredentialCreationOptionsAuthenticatorSelectionUserVerification {
#[serde(rename = "discouraged")]
Discouraged,
#[serde(rename = "preferred")]
Preferred,
#[serde(rename = "required")]
Required,
}
impl ::std::fmt::Display for CredentialCreationOptionsAuthenticatorSelectionUserVerification {
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 CredentialCreationOptionsAuthenticatorSelectionUserVerification {
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 CredentialCreationOptionsAuthenticatorSelectionUserVerification
{
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 CredentialCreationOptionsAuthenticatorSelectionUserVerification
{
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 CredentialCreationOptionsAuthenticatorSelectionUserVerification
{
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`CredentialCreationOptionsPubKeyCredParamsItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "alg",
/// "type"
/// ],
/// "properties": {
/// "alg": {
/// "description": "COSE algorithm identifier (e.g. -8 Ed25519, -7 ES256, -257 RS256).",
/// "type": "integer"
/// },
/// "type": {
/// "const": "public-key"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct CredentialCreationOptionsPubKeyCredParamsItem {
///COSE algorithm identifier (e.g. -8 Ed25519, -7 ES256, -257 RS256).
pub alg: i64,
#[serde(rename = "type")]
pub type_: ::serde_json::Value,
}
///`CredentialCreationOptionsRp`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "id",
/// "name"
/// ],
/// "properties": {
/// "id": {
/// "type": "string",
/// "minLength": 1
/// },
/// "name": {
/// "type": "string",
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct CredentialCreationOptionsRp {
pub id: CredentialCreationOptionsRpId,
pub name: CredentialCreationOptionsRpName,
}
///`CredentialCreationOptionsRpId`
///
/// <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 CredentialCreationOptionsRpId(::std::string::String);
impl ::std::ops::Deref for CredentialCreationOptionsRpId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<CredentialCreationOptionsRpId> for ::std::string::String {
fn from(value: CredentialCreationOptionsRpId) -> Self {
value.0
}
}
impl ::std::str::FromStr for CredentialCreationOptionsRpId {
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 CredentialCreationOptionsRpId {
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 CredentialCreationOptionsRpId {
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 CredentialCreationOptionsRpId {
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 CredentialCreationOptionsRpId {
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())
})
}
}
///`CredentialCreationOptionsRpName`
///
/// <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 CredentialCreationOptionsRpName(::std::string::String);
impl ::std::ops::Deref for CredentialCreationOptionsRpName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<CredentialCreationOptionsRpName> for ::std::string::String {
fn from(value: CredentialCreationOptionsRpName) -> Self {
value.0
}
}
impl ::std::str::FromStr for CredentialCreationOptionsRpName {
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 CredentialCreationOptionsRpName {
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 CredentialCreationOptionsRpName {
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 CredentialCreationOptionsRpName {
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 CredentialCreationOptionsRpName {
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())
})
}
}
///`CredentialCreationOptionsUser`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "displayName",
/// "id",
/// "name"
/// ],
/// "properties": {
/// "displayName": {
/// "type": "string"
/// },
/// "id": {
/// "description": "base64url-encoded user handle. SHOULD be a stable opaque identifier — never reuse a human-readable username here.",
/// "type": "string"
/// },
/// "name": {
/// "type": "string",
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct CredentialCreationOptionsUser {
#[serde(rename = "displayName")]
pub display_name: ::std::string::String,
///base64url-encoded user handle. SHOULD be a stable opaque identifier — never reuse a human-readable username here.
pub id: ::std::string::String,
pub name: CredentialCreationOptionsUserName,
}
///`CredentialCreationOptionsUserName`
///
/// <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 CredentialCreationOptionsUserName(::std::string::String);
impl ::std::ops::Deref for CredentialCreationOptionsUserName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<CredentialCreationOptionsUserName> for ::std::string::String {
fn from(value: CredentialCreationOptionsUserName) -> Self {
value.0
}
}
impl ::std::str::FromStr for CredentialCreationOptionsUserName {
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 CredentialCreationOptionsUserName {
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 CredentialCreationOptionsUserName {
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 CredentialCreationOptionsUserName {
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 CredentialCreationOptionsUserName {
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())
})
}
}
///`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)]
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,
}
///`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,
)]
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)]
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>,
}
///`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,
)]
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 a WebAuthn registration ceremony. The response carries PublicKeyCredentialCreationOptions; the producer hands them to `navigator.credentials.create({ publicKey: ... })`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://trusttasks.org/spec/auth/passkey/enroll/start/0.2",
/// "title": "Payload",
/// "description": "Ask the auth service to begin a WebAuthn registration ceremony. The response carries PublicKeyCredentialCreationOptions; the producer hands them to `navigator.credentials.create({ publicKey: ... })`.",
/// "type": "object",
/// "properties": {
/// "deviceLabel": {
/// "description": "Operator-facing label for the credential (e.g. \"Alice's MacBook\"). Surfaced in the credential list for later management.",
/// "type": "string"
/// },
/// "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)]
pub struct Payload {
///Operator-facing label for the credential (e.g. "Alice's MacBook"). Surfaced in the credential list for later management.
#[serde(
rename = "deviceLabel",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub device_label: ::std::option::Option<::std::string::String>,
///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 ::std::default::Default for Payload {
fn default() -> Self {
Self {
device_label: Default::default(),
ext: Default::default(),
}
}
}
///Server-issued WebAuthn creation options. Carried in a Trust Task document whose type is https://trusttasks.org/spec/auth/passkey/enroll/start/0.2#response.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Response",
/// "description": "Server-issued WebAuthn creation options. Carried in a Trust Task document whose type is https://trusttasks.org/spec/auth/passkey/enroll/start/0.2#response.",
/// "type": "object",
/// "required": [
/// "enrollmentId",
/// "options"
/// ],
/// "properties": {
/// "enrollmentId": {
/// "description": "Opaque server handle correlating this start with the matching finish. The producer MUST echo it verbatim.",
/// "type": "string",
/// "minLength": 1
/// },
/// "ext": {
/// "description": "Ecosystem-defined extension members per SPEC.md §4.5.1.",
/// "$ref": "#/definitions/Ext"
/// },
/// "options": {
/// "description": "PublicKeyCredentialCreationOptions for navigator.credentials.create.",
/// "$ref": "#/definitions/CredentialCreationOptions"
/// },
/// "uvOptions": {
/// "description": "New in 0.2. PublicKeyCredentialRequestOptions for a concurrent user-verification ceremony against the subject's EXISTING credentials. Present when the consumer requires re-authentication to add an authenticator — normally whenever the subject already has one. Absent for a first enrollment, when there is nothing to re-authenticate against. When present the producer MUST run both ceremonies and return both results to finish.",
/// "$ref": "#/definitions/CredentialRequestOptions"
/// }
/// },
/// "additionalProperties": false,
/// "$anchor": "response"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct Response {
///Opaque server handle correlating this start with the matching finish. The producer MUST echo it verbatim.
#[serde(rename = "enrollmentId")]
pub enrollment_id: ResponseEnrollmentId,
///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>,
///PublicKeyCredentialCreationOptions for navigator.credentials.create.
pub options: CredentialCreationOptions,
///New in 0.2. PublicKeyCredentialRequestOptions for a concurrent user-verification ceremony against the subject's EXISTING credentials. Present when the consumer requires re-authentication to add an authenticator — normally whenever the subject already has one. Absent for a first enrollment, when there is nothing to re-authenticate against. When present the producer MUST run both ceremonies and return both results to finish.
#[serde(
rename = "uvOptions",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub uv_options: ::std::option::Option<CredentialRequestOptions>,
}
///Opaque server handle correlating this start with the matching finish. The producer MUST echo it verbatim.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Opaque server handle correlating this start with the matching finish. The producer MUST echo it verbatim.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ResponseEnrollmentId(::std::string::String);
impl ::std::ops::Deref for ResponseEnrollmentId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ResponseEnrollmentId> for ::std::string::String {
fn from(value: ResponseEnrollmentId) -> Self {
value.0
}
}
impl ::std::str::FromStr for ResponseEnrollmentId {
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 ResponseEnrollmentId {
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 ResponseEnrollmentId {
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 ResponseEnrollmentId {
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 ResponseEnrollmentId {
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())
})
}
}
impl crate::Payload for Payload {
const TYPE_URI: &'static str = "https://trusttasks.org/spec/auth/passkey/enroll/start/0.2";
const IS_PROOF_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"CredentialCreationOptions\": {\n \"$anchor\": \"credentialCreationOptions\",\n \"additionalProperties\": false,\n \"description\": \"Server-issued options for `navigator.credentials.create({ publicKey: ... })`. Mirrors the WebAuthn Level 2 `PublicKeyCredentialCreationOptions` dictionary; binary fields are base64url-encoded strings (rather than ArrayBuffers) so the value is JSON-safe over the wire.\",\n \"properties\": {\n \"attestation\": {\n \"enum\": [\n \"none\",\n \"indirect\",\n \"direct\",\n \"enterprise\"\n ]\n },\n \"authenticatorSelection\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"authenticatorAttachment\": {\n \"enum\": [\n \"platform\",\n \"cross-platform\"\n ]\n },\n \"requireResidentKey\": {\n \"type\": \"boolean\"\n },\n \"residentKey\": {\n \"enum\": [\n \"discouraged\",\n \"preferred\",\n \"required\"\n ]\n },\n \"userVerification\": {\n \"enum\": [\n \"discouraged\",\n \"preferred\",\n \"required\"\n ]\n }\n },\n \"type\": \"object\"\n },\n \"challenge\": {\n \"description\": \"base64url-encoded one-time nonce.\",\n \"type\": \"string\"\n },\n \"excludeCredentials\": {\n \"items\": {\n \"$ref\": \"#/$defs/CredentialDescriptor\"\n },\n \"type\": \"array\"\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 \"pubKeyCredParams\": {\n \"items\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"alg\": {\n \"description\": \"COSE algorithm identifier (e.g. -8 Ed25519, -7 ES256, -257 RS256).\",\n \"type\": \"integer\"\n },\n \"type\": {\n \"const\": \"public-key\"\n }\n },\n \"required\": [\n \"type\",\n \"alg\"\n ],\n \"type\": \"object\"\n },\n \"minItems\": 1,\n \"type\": \"array\"\n },\n \"rp\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"id\": {\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"name\": {\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"id\",\n \"name\"\n ],\n \"type\": \"object\"\n },\n \"timeout\": {\n \"minimum\": 1,\n \"type\": \"integer\"\n },\n \"user\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"displayName\": {\n \"type\": \"string\"\n },\n \"id\": {\n \"description\": \"base64url-encoded user handle. SHOULD be a stable opaque identifier — never reuse a human-readable username here.\",\n \"type\": \"string\"\n },\n \"name\": {\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"id\",\n \"name\",\n \"displayName\"\n ],\n \"type\": \"object\"\n }\n },\n \"required\": [\n \"challenge\",\n \"rp\",\n \"user\",\n \"pubKeyCredParams\"\n ],\n \"title\": \"PublicKeyCredentialCreationOptions\",\n \"type\": \"object\"\n },\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 WebAuthn creation options. Carried in a Trust Task document whose type is https://trusttasks.org/spec/auth/passkey/enroll/start/0.2#response.\",\n \"properties\": {\n \"enrollmentId\": {\n \"description\": \"Opaque server handle correlating this start with the matching finish. The producer MUST echo it verbatim.\",\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 \"options\": {\n \"$ref\": \"#/$defs/CredentialCreationOptions\",\n \"description\": \"PublicKeyCredentialCreationOptions for navigator.credentials.create.\"\n },\n \"uvOptions\": {\n \"$ref\": \"#/$defs/CredentialRequestOptions\",\n \"description\": \"New in 0.2. PublicKeyCredentialRequestOptions for a concurrent user-verification ceremony against the subject's EXISTING credentials. Present when the consumer requires re-authentication to add an authenticator — normally whenever the subject already has one. Absent for a first enrollment, when there is nothing to re-authenticate against. When present the producer MUST run both ceremonies and return both results to finish.\"\n }\n },\n \"required\": [\n \"enrollmentId\",\n \"options\"\n ],\n \"title\": \"Auth Passkey Enroll Start — response payload\",\n \"type\": \"object\"\n }\n },\n \"$id\": \"https://trusttasks.org/spec/auth/passkey/enroll/start/0.2\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"additionalProperties\": false,\n \"description\": \"Ask the auth service to begin a WebAuthn registration ceremony. The response carries PublicKeyCredentialCreationOptions; the producer hands them to `navigator.credentials.create({ publicKey: ... })`.\",\n \"properties\": {\n \"deviceLabel\": {\n \"description\": \"Operator-facing label for the credential (e.g. \\\"Alice's MacBook\\\"). Surfaced in the credential list for later management.\",\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 \"title\": \"Auth — Passkey Enroll (start)\",\n \"type\": \"object\"\n}\n",
);
}
impl crate::Payload for Response {
const TYPE_URI: &'static str =
"https://trusttasks.org/spec/auth/passkey/enroll/start/0.2#response";
const IS_PROOF_REQUIRED: bool = true;
const IS_RECIPIENT_REQUIRED: bool = true;
const PAYLOAD_SCHEMA: Option<&'static str> = Some(
"{\n \"$defs\": {\n \"CredentialCreationOptions\": {\n \"$anchor\": \"credentialCreationOptions\",\n \"additionalProperties\": false,\n \"description\": \"Server-issued options for `navigator.credentials.create({ publicKey: ... })`. Mirrors the WebAuthn Level 2 `PublicKeyCredentialCreationOptions` dictionary; binary fields are base64url-encoded strings (rather than ArrayBuffers) so the value is JSON-safe over the wire.\",\n \"properties\": {\n \"attestation\": {\n \"enum\": [\n \"none\",\n \"indirect\",\n \"direct\",\n \"enterprise\"\n ]\n },\n \"authenticatorSelection\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"authenticatorAttachment\": {\n \"enum\": [\n \"platform\",\n \"cross-platform\"\n ]\n },\n \"requireResidentKey\": {\n \"type\": \"boolean\"\n },\n \"residentKey\": {\n \"enum\": [\n \"discouraged\",\n \"preferred\",\n \"required\"\n ]\n },\n \"userVerification\": {\n \"enum\": [\n \"discouraged\",\n \"preferred\",\n \"required\"\n ]\n }\n },\n \"type\": \"object\"\n },\n \"challenge\": {\n \"description\": \"base64url-encoded one-time nonce.\",\n \"type\": \"string\"\n },\n \"excludeCredentials\": {\n \"items\": {\n \"$ref\": \"#/$defs/CredentialDescriptor\"\n },\n \"type\": \"array\"\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 \"pubKeyCredParams\": {\n \"items\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"alg\": {\n \"description\": \"COSE algorithm identifier (e.g. -8 Ed25519, -7 ES256, -257 RS256).\",\n \"type\": \"integer\"\n },\n \"type\": {\n \"const\": \"public-key\"\n }\n },\n \"required\": [\n \"type\",\n \"alg\"\n ],\n \"type\": \"object\"\n },\n \"minItems\": 1,\n \"type\": \"array\"\n },\n \"rp\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"id\": {\n \"minLength\": 1,\n \"type\": \"string\"\n },\n \"name\": {\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"id\",\n \"name\"\n ],\n \"type\": \"object\"\n },\n \"timeout\": {\n \"minimum\": 1,\n \"type\": \"integer\"\n },\n \"user\": {\n \"additionalProperties\": false,\n \"properties\": {\n \"displayName\": {\n \"type\": \"string\"\n },\n \"id\": {\n \"description\": \"base64url-encoded user handle. SHOULD be a stable opaque identifier — never reuse a human-readable username here.\",\n \"type\": \"string\"\n },\n \"name\": {\n \"minLength\": 1,\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"id\",\n \"name\",\n \"displayName\"\n ],\n \"type\": \"object\"\n }\n },\n \"required\": [\n \"challenge\",\n \"rp\",\n \"user\",\n \"pubKeyCredParams\"\n ],\n \"title\": \"PublicKeyCredentialCreationOptions\",\n \"type\": \"object\"\n },\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 WebAuthn creation options. Carried in a Trust Task document whose type is https://trusttasks.org/spec/auth/passkey/enroll/start/0.2#response.\",\n \"properties\": {\n \"enrollmentId\": {\n \"description\": \"Opaque server handle correlating this start with the matching finish. The producer MUST echo it verbatim.\",\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 \"options\": {\n \"$ref\": \"#/$defs/CredentialCreationOptions\",\n \"description\": \"PublicKeyCredentialCreationOptions for navigator.credentials.create.\"\n },\n \"uvOptions\": {\n \"$ref\": \"#/$defs/CredentialRequestOptions\",\n \"description\": \"New in 0.2. PublicKeyCredentialRequestOptions for a concurrent user-verification ceremony against the subject's EXISTING credentials. Present when the consumer requires re-authentication to add an authenticator — normally whenever the subject already has one. Absent for a first enrollment, when there is nothing to re-authenticate against. When present the producer MUST run both ceremonies and return both results to finish.\"\n }\n },\n \"required\": [\n \"enrollmentId\",\n \"options\"\n ],\n \"title\": \"Auth Passkey Enroll Start — response payload\",\n \"type\": \"object\"\n }\n },\n \"$ref\": \"#/$defs/Response\",\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\n}\n",
);
}
#[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\": \"33333333-dddd-eeee-ffff-444444444444\",\n \"type\": \"https://trusttasks.org/spec/auth/passkey/enroll/start/0.2#response\",\n \"threadId\": \"11111111-aaaa-bbbb-cccc-222222222222\",\n \"issuer\": \"did:web:auth.example\",\n \"recipient\": \"did:web:alice.example\",\n \"issuedAt\": \"2026-05-23T12:00:01Z\",\n \"payload\": {\n \"enrollmentId\": \"enr_1a2b3c4d5e6f7890\",\n \"options\": {\n \"challenge\": \"Zm9vYmFyYmF6cXV4\",\n \"rp\": { \"id\": \"auth.example\", \"name\": \"Auth Example\" },\n \"user\": {\n \"id\": \"dXNyXzhmMmMxZDRlOWE3YjMwNTY\",\n \"name\": \"alice\",\n \"displayName\": \"Alice\"\n },\n \"pubKeyCredParams\": [\n { \"type\": \"public-key\", \"alg\": -8 },\n { \"type\": \"public-key\", \"alg\": -7 }\n ],\n \"timeout\": 60000,\n \"attestation\": \"none\",\n \"authenticatorSelection\": {\n \"residentKey\": \"preferred\",\n \"userVerification\": \"preferred\"\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");
}
#[test]
fn response_example_2() {
const JSON: &str = "{\n \"id\": \"55555555-eeee-ffff-0000-666666666666\",\n \"type\": \"https://trusttasks.org/spec/auth/passkey/enroll/start/0.2#response\",\n \"threadId\": \"11111111-aaaa-bbbb-cccc-222222222222\",\n \"issuer\": \"did:web:auth.example\",\n \"recipient\": \"did:web:alice.example\",\n \"issuedAt\": \"2026-07-27T12:00:01Z\",\n \"payload\": {\n \"enrollmentId\": \"enr_9e8d7c6b5a493827\",\n \"options\": {\n \"challenge\": \"ZW5yb2xsLWNoYWxsZW5nZQ\",\n \"rp\": { \"id\": \"auth.example\", \"name\": \"Auth Example\" },\n \"user\": {\n \"id\": \"dXNyXzhmMmMxZDRlOWE3YjMwNTY\",\n \"name\": \"alice\",\n \"displayName\": \"Alice\"\n },\n \"pubKeyCredParams\": [{ \"type\": \"public-key\", \"alg\": -8 }],\n \"excludeCredentials\": [\n { \"type\": \"public-key\", \"id\": \"q1w2e3r4t5y6u7i8o9p0\" }\n ]\n },\n \"uvOptions\": {\n \"challenge\": \"cmVhdXRoLWNoYWxsZW5nZQ\",\n \"rpId\": \"auth.example\",\n \"userVerification\": \"required\",\n \"allowCredentials\": [\n { \"type\": \"public-key\", \"id\": \"q1w2e3r4t5y6u7i8o9p0\", \"transports\": [\"internal\"] }\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)] = &[
(
"Unknown top-level payload member.",
"{\n \"rpId\": \"example.com\"\n}",
),
(
"Bare/unnamespaced ext key.",
"{\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
);
}
}
}