commit 9d4b0de0577d131bba754f47c096c6a70e2b3f8c
parent 06862a895a528d5842d3ec5ca4f10823755fb0fe
Author: Zack Newman <zack@philomathiclife.com>
Date: Wed, 1 Apr 2026 09:05:14 -0600
remove PRECIS-enforcement
Diffstat:
10 files changed, 218 insertions(+), 1141 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -121,7 +121,6 @@ hashbrown = { version = "0.16.1", default-features = false }
ml-dsa = { version = "0.1.0-rc.8", default-features = false }
p256 = { version = "0.14.0-rc.8", default-features = false, features = ["ecdsa"] }
p384 = { version = "0.14.0-rc.8", default-features = false, features = ["ecdsa"] }
-precis-profiles = { version = "0.1.13", default-features = false }
rand = { version = "0.10.0", default-features = false, features = ["thread_rng"] }
rsa = { version = "0.10.0-rc.17", default-features = false, features = ["encoding", "sha2"] }
serde = { version = "1.0.228", default-features = false, features = ["alloc"], optional = true }
diff --git a/README.md b/README.md
@@ -22,13 +22,13 @@ use webauthn_rp::{
AuthenticatedCredential64, DiscoverableAuthentication64, DiscoverableAuthenticationServerState,
DiscoverableCredentialRequestOptions, CredentialCreationOptions64, RegisteredCredential64,
Registration, RegistrationServerState64,
- hash::hash_set::FixedCapHashSet,
+ hash::hash_set::{InsertRemoveExpired, MaxLenHashSet},
request::{
- AsciiDomainStatic, PublicKeyCredentialDescriptor, RpId,
+ PublicKeyCredentialDescriptor, RpId,
auth::AuthenticationVerificationOptions,
register::{
- Nickname, PublicKeyCredentialUserEntity64, RegistrationVerificationOptions,
- UserHandle64, Username,
+ PublicKeyCredentialUserEntity64, RegistrationVerificationOptions,
+ UserHandle64,
},
},
response::{
@@ -40,7 +40,7 @@ use webauthn_rp::{
use serde::de::{Deserialize, Deserializer};
use serde_json::Error as JsonErr;
/// The RP ID our application uses.
-const RP_ID: &RpId = &RpId::StaticDomain(AsciiDomainStatic::new("example.com").unwrap());
+const RP_ID: &RpId = &RpId::from_static_domain("example.com").unwrap();
/// The registration verification options.
const REG_OPTS: &RegistrationVerificationOptions::<'static, 'static, &'static str, &'static str> = &RegistrationVerificationOptions::new();
/// The authentication verification options.
@@ -84,12 +84,12 @@ impl From<AuthCeremonyErr> for AppErr {
/// This gets sent from the user after an account is created on their side. The registration ceremony
/// still has to be successfully completed for the account to be created server side. In the event of an error,
/// the user should delete the created passkey since it won't be usable.
-struct AccountReg<'a, 'b> {
+struct AccountReg {
registration: Registration,
- user_name: Username<'a>,
- user_display_name: Nickname<'b>,
+ user_name: String,
+ user_display_name: String,
}
-impl<'de: 'a + 'b, 'a, 'b> Deserialize<'de> for AccountReg<'a, 'b> {
+impl<'de> Deserialize<'de> for AccountReg {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
@@ -103,18 +103,18 @@ impl<'de: 'a + 'b, 'a, 'b> Deserialize<'de> for AccountReg<'a, 'b> {
/// already exist otherwise. This is similar to credential creation except a random `UserHandle64` is generated and
/// will be used for subsequent credential registrations.
fn start_account_creation(
- reg_ceremonies: &mut FixedCapHashSet<RegistrationServerState64>,
+ reg_ceremonies: &mut MaxLenHashSet<RegistrationServerState64>,
) -> Result<Vec<u8>, AppErr> {
let user_id = UserHandle64::new();
let (server, client) =
- CredentialCreationOptions64::first_passkey_with_blank_user_info(
- RP_ID, &user_id,
+ CredentialCreationOptions64::passkey(
+ RP_ID, PublicKeyCredentialUserEntity64 { id: &user_id, name: "", display_name: "", }, Vec::new()
)
.start_ceremony()
.unwrap_or_else(|_e| {
unreachable!("we don't manually mutate the options and we assume the server clock is functioning; thus this won't error")
});
- if reg_ceremonies.insert_remove_all_expired(server).is_some_and(convert::identity)
+ if matches!(reg_ceremonies.insert_remove_all_expired(server), InsertRemoveExpired::Success)
{
Ok(serde_json::to_vec(&client)
.unwrap_or_else(|_e| unreachable!("bug in RegistrationClientState64::serialize")))
@@ -131,10 +131,10 @@ fn start_account_creation(
/// Note if this errors, then the user should be notified to delete the passkey created on their
/// authenticator.
fn finish_account_creation(
- reg_ceremonies: &mut FixedCapHashSet<RegistrationServerState64>,
+ reg_ceremonies: &mut MaxLenHashSet<RegistrationServerState64>,
client_data: &[u8],
) -> Result<(), AppErr> {
- let account = serde_json::from_slice::<AccountReg<'_, '_>>(client_data)?;
+ let account = serde_json::from_slice::<AccountReg>(client_data)?;
insert_account(
&account,
reg_ceremonies
@@ -156,15 +156,15 @@ fn finish_account_creation(
/// the authenticator.
fn start_cred_registration(
user_id: &UserHandle64,
- reg_ceremonies: &mut FixedCapHashSet<RegistrationServerState64>,
+ reg_ceremonies: &mut MaxLenHashSet<RegistrationServerState64>,
) -> Result<Vec<u8>, AppErr> {
- let (entity, creds) = select_user_info(user_id)?.ok_or(AppErr::NoAccount)?;
- let (server, client) = CredentialCreationOptions64::passkey(RP_ID, entity, creds)
+ let (username, user_display_name, creds) = select_user_info(user_id)?.ok_or(AppErr::NoAccount)?;
+ let (server, client) = CredentialCreationOptions64::passkey(RP_ID, PublicKeyCredentialUserEntity64 { name: &username, id: user_id, display_name: &user_display_name, }, creds)
.start_ceremony()
.unwrap_or_else(|_e| {
unreachable!("we don't manually mutate the options and we assume the server clock is functioning; thus this won't error")
});
- if reg_ceremonies.insert_remove_all_expired(server).is_some_and(convert::identity)
+ if matches!(reg_ceremonies.insert_remove_all_expired(server), InsertRemoveExpired::Success)
{
Ok(serde_json::to_vec(&client)
.unwrap_or_else(|_e| unreachable!("bug in RegistrationClientState64::serialize")))
@@ -181,7 +181,7 @@ fn start_cred_registration(
/// Note if this errors, then the user should be notified to delete the passkey created on their
/// authenticator.
fn finish_cred_registration(
- reg_ceremonies: &mut FixedCapHashSet<RegistrationServerState64>,
+ reg_ceremonies: &mut MaxLenHashSet<RegistrationServerState64>,
client_data: &[u8],
) -> Result<(), AppErr> {
// `Registration::from_json_custom` is available iff `serde_relaxed` is enabled.
@@ -200,14 +200,14 @@ fn finish_cred_registration(
}
/// Starts the passkey authentication ceremony.
fn start_auth(
- auth_ceremonies: &mut FixedCapHashSet<DiscoverableAuthenticationServerState>,
+ auth_ceremonies: &mut MaxLenHashSet<DiscoverableAuthenticationServerState>,
) -> Result<Vec<u8>, AppErr> {
let (server, client) = DiscoverableCredentialRequestOptions::passkey(RP_ID)
.start_ceremony()
.unwrap_or_else(|_e| {
unreachable!("we don't manually mutate the options and we assume the server clock is functioning; thus this won't error")
});
- if auth_ceremonies.insert_remove_all_expired(server).is_some_and(convert::identity)
+ if matches!(auth_ceremonies.insert_remove_all_expired(server), InsertRemoveExpired::Success)
{
Ok(serde_json::to_vec(&client).unwrap_or_else(|_e| {
unreachable!("bug in DiscoverableAuthenticationClientState::serialize")
@@ -218,7 +218,7 @@ fn start_auth(
}
/// Finishes the passkey authentication ceremony.
fn finish_auth(
- auth_ceremonies: &mut FixedCapHashSet<DiscoverableAuthenticationServerState>,
+ auth_ceremonies: &mut MaxLenHashSet<DiscoverableAuthenticationServerState>,
client_data: &[u8],
) -> Result<(), AppErr> {
// `DiscoverableAuthentication64::from_json_custom` is available iff `serde_relaxed` is enabled.
@@ -252,7 +252,7 @@ fn finish_auth(
/// Errors iff writing `account` or `cred` errors, there already exists a credential using the same
/// `CredentialId`, or there already exists an account using the same `UserHandle64`.
fn insert_account(
- account: &AccountReg<'_, '_>,
+ account: &AccountReg,
cred: RegisteredCredential64<'_>,
) -> Result<(), AppErr> {
// ⋮
@@ -266,8 +266,9 @@ fn select_user_info(
user_id: &UserHandle64,
) -> Result<
Option<(
- PublicKeyCredentialUserEntity64<'static, 'static, '_>,
- Vec<PublicKeyCredentialDescriptor<Vec<u8>>>,
+ String,
+ String,
+ Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
)>,
AppErr,
> {
@@ -330,9 +331,8 @@ unsuitable or only partially suitable (e.g., human-readable output is desired),
[`custom`](#custom) to allow construction of certain types (e.g., `AuthenticatedCredential`).
If possible and desired, one may wish to save the data "directly" to avoid any potential temporary allocations.
-For example `StaticState::encode` will return a `Vec` containing hundreds (and possibly thousands in the
-extreme case) of bytes if the underlying public key is an RSA key. This additional allocation and copy of data
-is obviously avoided if `StaticState` is stored as a
+For example `StaticState::encode` will return a `Vec` containing thousands of bytes if the underlying public key
+is an ML-DSA key. This additional allocation and copy of data is obviously avoided if `StaticState` is stored as a
[composite type](https://www.postgresql.org/docs/current/rowtypes.html) or its fields are stored in separate
columns when written to a relational database (RDB).
@@ -409,7 +409,7 @@ storing such data in memory:
increasing. If data resides in memory, a monotonic `Instant` can be used instead.
It is for those reasons data like `RegistrationServerState` are not serializable by default and require the
-use of in-memory collections (e.g., `FixedCapHashSet`). To better ensure OOM is not a concern, RPs should set
+use of in-memory collections (e.g., `MaxLenHashSet`). To better ensure OOM is not a concern, RPs should set
reasonable timeouts. Since ceremonies can only be completed by moving data (e.g.,
`RegistrationServerState::verify`), ceremony completion is guaranteed to free up the memory used—
`RegistrationServerState` instances are as small as 48 bytes on `x86_64-unknown-linux-gnu` platforms. To avoid
@@ -425,6 +425,12 @@ not guaranteed to be the server that finishes the ceremony.
The only supported signature algorithms are the following:
+* ML-DSA-87 as defined in [NIST FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf). This
+ corresponds to `CoseAlgorithmIdentifier::Mldsa87`.
+* ML-DSA-65 as defined in [NIST FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf). This
+ corresponds to `CoseAlgorithmIdentifier::Mldsa65`.
+* ML-DSA-44 as defined in [NIST FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf). This
+ corresponds to `CoseAlgorithmIdentifier::Mldsa44`.
* Ed25519 as defined in [RFC 8032 § 5.1](https://www.rfc-editor.org/rfc/rfc8032#section-5.1). This corresponds
to `CoseAlgorithmIdentifier::Eddsa`.
* ECDSA as defined in [SEC 1 Version 2.0 § 4.1](https://www.secg.org/sec1-v2.pdf#subsection.4.1) using SHA-256
@@ -445,8 +451,7 @@ the following ways:
* [CTAP2 canonical CBOR encoding form](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#ctap2-canonical-cbor-encoding-form).
* `Deserialize` implementations requiring _exact_ conformance (e.g., not allowing unknown data).
* More thorough interrelated data validation (e.g., all places a Credential ID exists must match).
-* Implement a lot of recommended (i.e., SHOULD) criteria (e.g.,
- [User display names conforming to the Nickname Profile as defined in RFC 8266](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialentity-name)).
+* Implement a lot of recommended (i.e., SHOULD) criteria.
Unfortunately like almost all software, this library has not been formally verified; however great care is
employed in the following ways:
diff --git a/src/lib.rs b/src/lib.rs
@@ -27,8 +27,8 @@
//! PublicKeyCredentialDescriptor, RpId,
//! auth::AuthenticationVerificationOptions,
//! register::{
-//! DisplayName, PublicKeyCredentialUserEntity64, RegistrationVerificationOptions,
-//! UserHandle64, Username,
+//! PublicKeyCredentialUserEntity64, RegistrationVerificationOptions,
+//! UserHandle64,
//! },
//! },
//! response::{
@@ -88,13 +88,13 @@
//! /// This gets sent from the user after an account is created on their side. The registration ceremony
//! /// still has to be successfully completed for the account to be created server side. In the event of an error,
//! /// the user should delete the created passkey since it won't be usable.
-//! struct AccountReg<'a, 'b> {
+//! struct AccountReg {
//! registration: Registration,
-//! user_name: Username<'a>,
-//! user_display_name: DisplayName<'b>,
+//! user_name: String,
+//! user_display_name: String,
//! }
//! # #[cfg(feature = "serde")]
-//! impl<'de: 'a + 'b, 'a, 'b> Deserialize<'de> for AccountReg<'a, 'b> {
+//! impl<'de> Deserialize<'de> for AccountReg {
//! fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
//! where
//! D: Deserializer<'de>,
@@ -113,11 +113,9 @@
//! reg_ceremonies: &mut MaxLenHashSet<RegistrationServerState64>,
//! ) -> Result<Vec<u8>, AppErr> {
//! let user_id = UserHandle64::new();
-//! let user_name = Username::try_from("blank").unwrap();
-//! let user_display_name = DisplayName::Blank;
//! let (server, client) =
//! CredentialCreationOptions64::passkey(
-//! RP_ID, PublicKeyCredentialUserEntity64 { id: &user_id, name: user_name, display_name: user_display_name, }, Vec::new()
+//! RP_ID, PublicKeyCredentialUserEntity64 { id: &user_id, name: "", display_name: "", }, Vec::new()
//! )
//! .start_ceremony()
//! .unwrap_or_else(|_e| {
@@ -144,7 +142,7 @@
//! reg_ceremonies: &mut MaxLenHashSet<RegistrationServerState64>,
//! client_data: &[u8],
//! ) -> Result<(), AppErr> {
-//! let account = serde_json::from_slice::<AccountReg<'_, '_>>(client_data)?;
+//! let account = serde_json::from_slice::<AccountReg>(client_data)?;
//! insert_account(
//! &account,
//! reg_ceremonies
@@ -169,8 +167,8 @@
//! user_id: &UserHandle64,
//! reg_ceremonies: &mut MaxLenHashSet<RegistrationServerState64>,
//! ) -> Result<Vec<u8>, AppErr> {
-//! let (entity, creds) = select_user_info(user_id)?.ok_or(AppErr::NoAccount)?;
-//! let (server, client) = CredentialCreationOptions64::passkey(RP_ID, entity, creds)
+//! let (username, user_display_name, creds) = select_user_info(user_id)?.ok_or(AppErr::NoAccount)?;
+//! let (server, client) = CredentialCreationOptions64::passkey(RP_ID, PublicKeyCredentialUserEntity64 { name: &username, id: user_id, display_name: &user_display_name, }, creds)
//! .start_ceremony()
//! .unwrap_or_else(|_e| {
//! unreachable!("we don't manually mutate the options and we assume the server clock is functioning; thus this won't error")
@@ -266,7 +264,7 @@
//! /// Errors iff writing `account` or `cred` errors, there already exists a credential using the same
//! /// `CredentialId`, or there already exists an account using the same `UserHandle64`.
//! fn insert_account(
-//! account: &AccountReg<'_, '_>,
+//! account: &AccountReg,
//! cred: RegisteredCredential64<'_>,
//! ) -> Result<(), AppErr> {
//! // ⋮
@@ -277,11 +275,12 @@
//! /// # Errors
//! ///
//! /// Errors iff fetching the data errors.
-//! fn select_user_info<'name, 'display_name>(
+//! fn select_user_info(
//! user_id: &UserHandle64,
//! ) -> Result<
//! Option<(
-//! PublicKeyCredentialUserEntity64<'name, 'display_name, '_>,
+//! String,
+//! String,
//! Vec<PublicKeyCredentialDescriptor<Box<[u8]>>>,
//! )>,
//! AppErr,
@@ -350,11 +349,10 @@
//! [`custom`](#custom) to allow construction of certain types (e.g., [`AuthenticatedCredential`]).
//!
//! If possible and desired, one may wish to save the data "directly" to avoid any potential temporary allocations.
-//! For example [`StaticState::encode`] will return a [`Vec`] containing hundreds (and possibly thousands in the
-//! extreme case) of bytes if the underlying public key is an RSA key. This additional allocation and copy of data
-//! is obviously avoided if [`StaticState`] is stored as a
-//! [composite type](https://www.postgresql.org/docs/current/rowtypes.html) or its fields are stored in separate
-//! columns when written to a relational database (RDB).
+//! For example [`StaticState::encode`] will return a [`Vec`] containing thousands of bytes if the underlying
+//! public key is an ML-DSA key. This additional allocation and copy of data is obviously avoided if
+//! [`StaticState`] is stored as a [composite type](https://www.postgresql.org/docs/current/rowtypes.html) or its
+//! fields are stored in separate columns when written to a relational database (RDB).
//!
//! ### `custom`
//!
@@ -446,6 +444,12 @@
//!
//! The only supported signature algorithms are the following:
//!
+//! * ML-DSA-87 as defined in [NIST FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf). This
+//! corresponds to [`CoseAlgorithmIdentifier::Mldsa87`].
+//! * ML-DSA-65 as defined in [NIST FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf). This
+//! corresponds to [`CoseAlgorithmIdentifier::Mldsa65`].
+//! * ML-DSA-44 as defined in [NIST FIPS 204](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.204.pdf). This
+//! corresponds to [`CoseAlgorithmIdentifier::Mldsa44`].
//! * Ed25519 as defined in [RFC 8032 § 5.1](https://www.rfc-editor.org/rfc/rfc8032#section-5.1). This corresponds
//! to [`CoseAlgorithmIdentifier::Eddsa`].
//! * ECDSA as defined in [SEC 1 Version 2.0 § 4.1](https://www.secg.org/sec1-v2.pdf#subsection.4.1) using SHA-256
@@ -466,8 +470,7 @@
//! * [CTAP2 canonical CBOR encoding form](https://fidoalliance.org/specs/fido-v2.2-rd-20230321/fido-client-to-authenticator-protocol-v2.2-rd-20230321.html#ctap2-canonical-cbor-encoding-form).
//! * `Deserialize` implementations requiring _exact_ conformance (e.g., not allowing unknown data).
//! * More thorough interrelated data validation (e.g., all places a Credential ID exists must match).
-//! * Implement a lot of recommended (i.e., SHOULD) criteria (e.g.,
-//! [User display names conforming to the Nickname Profile as defined in RFC 8266](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialentity-name)).
+//! * Implement a lot of recommended (i.e., SHOULD) criteria.
//!
//! Unfortunately like almost all software, this library has not been formally verified; however great care is
//! employed in the following ways:
@@ -527,6 +530,11 @@ use crate::request::{
use crate::response::error::CredentialIdErr;
#[cfg(feature = "serde_relaxed")]
use crate::response::ser_relaxed::SerdeJsonErr;
+#[cfg(feature = "bin")]
+use crate::response::{
+ bin::DecodeAuthTransportsErr,
+ register::bin::{DecodeDynamicStateErr, DecodeStaticStateErr},
+};
#[cfg(doc)]
use crate::{
hash::hash_set::MaxLenHashSet,
@@ -535,8 +543,8 @@ use crate::{
TimedCeremony, Url,
auth::{AllowedCredential, AllowedCredentials, PublicKeyCredentialRequestOptions},
register::{
- CoseAlgorithmIdentifier, DisplayName, Nickname, PublicKeyCredentialCreationOptions,
- PublicKeyCredentialUserEntity, UserHandle16, UserHandle64, Username,
+ CoseAlgorithmIdentifier, PublicKeyCredentialCreationOptions,
+ PublicKeyCredentialUserEntity, UserHandle16, UserHandle64,
},
},
response::{
@@ -549,21 +557,12 @@ use crate::{
},
},
};
-#[cfg(feature = "bin")]
-use crate::{
- request::register::bin::{DecodeDisplayNameErr, DecodeUsernameErr},
- response::{
- bin::DecodeAuthTransportsErr,
- register::bin::{DecodeDynamicStateErr, DecodeStaticStateErr},
- },
-};
use crate::{
request::{
auth::error::{InvalidTimeout, NonDiscoverableCredentialRequestOptionsErr},
error::{AsciiDomainErr, DomainOriginParseErr, PortParseErr, SchemeParseErr, UrlErr},
register::{
- ResidentKeyRequirement, USER_HANDLE_MAX_LEN, UserHandle,
- error::{CreationOptionsErr, NicknameErr, UsernameErr},
+ ResidentKeyRequirement, USER_HANDLE_MAX_LEN, UserHandle, error::CreationOptionsErr,
},
},
response::{
@@ -829,7 +828,7 @@ fn verify_static_and_dynamic_state<T>(
/// Note that [`RpId`] and user information other than the `UserHandle` are not stored in `RegisteredCredential`.
/// RPs that wish to store such information must do so on their own. Since user information is likely the same
/// for a given `UserHandle` and `RpId` is likely static, it makes little sense to store such information
-/// automatically. Types like [`Username`] implement `Encode` and `Decode` to assist such a thing.
+/// automatically.
///
/// When registering a credential, [`AttestedCredentialData::aaguid`], [`AttestedCredentialData::credential_id`],
/// and [`AttestedCredentialData::credential_public_key`] will be the sources for [`Metadata::aaguid`],
@@ -1140,10 +1139,6 @@ pub enum AggErr {
CreationOptions(CreationOptionsErr),
/// Variant when [`NonDiscoverableCredentialRequestOptions::start_ceremony`] errors.
NonDiscoverableCredentialRequestOptions(NonDiscoverableCredentialRequestOptionsErr),
- /// Variant when [`Nickname::try_from`] errors.
- Nickname(NicknameErr),
- /// Variant when [`Username::try_from`] errors.
- Username(UsernameErr),
/// Variant when [`RegistrationServerState::verify`] errors.
RegCeremony(RegCeremonyErr),
/// Variant when [`DiscoverableAuthenticationServerState::verify`] or.
@@ -1172,12 +1167,6 @@ pub enum AggErr {
/// Variant when [`DynamicState::decode`] errors.
#[cfg(feature = "bin")]
DecodeDynamicState(DecodeDynamicStateErr),
- /// Variant when [`DisplayName::decode`] errors.
- #[cfg(feature = "bin")]
- DecodeDisplayName(DecodeDisplayNameErr),
- /// Variant when [`Username::decode`] errors.
- #[cfg(feature = "bin")]
- DecodeUsername(DecodeUsernameErr),
/// Variant when [`RegistrationServerState::decode`] errors.
#[cfg(feature = "serializable_server_state")]
DecodeRegistrationServerState(DecodeRegistrationServerStateErr),
@@ -1263,18 +1252,6 @@ impl From<NonDiscoverableCredentialRequestOptionsErr> for AggErr {
Self::NonDiscoverableCredentialRequestOptions(value)
}
}
-impl From<NicknameErr> for AggErr {
- #[inline]
- fn from(value: NicknameErr) -> Self {
- Self::Nickname(value)
- }
-}
-impl From<UsernameErr> for AggErr {
- #[inline]
- fn from(value: UsernameErr) -> Self {
- Self::Username(value)
- }
-}
impl From<RegCeremonyErr> for AggErr {
#[inline]
fn from(value: RegCeremonyErr) -> Self {
@@ -1345,20 +1322,6 @@ impl From<DecodeDynamicStateErr> for AggErr {
Self::DecodeDynamicState(value)
}
}
-#[cfg(feature = "bin")]
-impl From<DecodeDisplayNameErr> for AggErr {
- #[inline]
- fn from(value: DecodeDisplayNameErr) -> Self {
- Self::DecodeDisplayName(value)
- }
-}
-#[cfg(feature = "bin")]
-impl From<DecodeUsernameErr> for AggErr {
- #[inline]
- fn from(value: DecodeUsernameErr) -> Self {
- Self::DecodeUsername(value)
- }
-}
#[cfg(feature = "serializable_server_state")]
impl From<DecodeRegistrationServerStateErr> for AggErr {
#[inline]
@@ -1427,8 +1390,6 @@ impl Display for AggErr {
Self::InvalidTimeout(err) => err.fmt(f),
Self::CreationOptions(err) => err.fmt(f),
Self::NonDiscoverableCredentialRequestOptions(err) => err.fmt(f),
- Self::Nickname(err) => err.fmt(f),
- Self::Username(err) => err.fmt(f),
Self::RegCeremony(ref err) => err.fmt(f),
Self::AuthCeremony(ref err) => err.fmt(f),
Self::AttestationObject(err) => err.fmt(f),
@@ -1444,10 +1405,6 @@ impl Display for AggErr {
Self::DecodeStaticState(err) => err.fmt(f),
#[cfg(feature = "bin")]
Self::DecodeDynamicState(err) => err.fmt(f),
- #[cfg(feature = "bin")]
- Self::DecodeDisplayName(err) => err.fmt(f),
- #[cfg(feature = "bin")]
- Self::DecodeUsername(err) => err.fmt(f),
#[cfg(feature = "serializable_server_state")]
Self::DecodeRegistrationServerState(err) => err.fmt(f),
#[cfg(feature = "serializable_server_state")]
diff --git a/src/request.rs b/src/request.rs
@@ -106,7 +106,7 @@ pub mod error;
/// # hash::hash_set::{InsertRemoveExpired, MaxLenHashSet},
/// # request::{
/// # register::{
-/// # CredentialCreationOptions, DisplayName, PublicKeyCredentialUserEntity, UserHandle, USER_HANDLE_MAX_LEN, UserHandle64,
+/// # CredentialCreationOptions, PublicKeyCredentialUserEntity, UserHandle, USER_HANDLE_MAX_LEN, UserHandle64,
/// # },
/// # PublicKeyCredentialDescriptor, RpId
/// # },
@@ -154,9 +154,9 @@ pub mod error;
/// fn get_user_entity(user: &UserHandle64) -> Result<PublicKeyCredentialUserEntity<'_, '_, '_, USER_HANDLE_MAX_LEN>, AggErr> {
/// // ⋮
/// # Ok(PublicKeyCredentialUserEntity {
-/// # name: "foo".try_into()?,
+/// # name: "foo",
/// # id: user,
-/// # display_name: DisplayName::Blank,
+/// # display_name: "",
/// # })
/// }
/// /// Fetch the `PublicKeyCredentialDescriptor`s associated with `user`.
@@ -1902,9 +1902,8 @@ mod tests {
Extension as AuthExt, NonDiscoverableCredentialRequestOptions, PrfInputOwned,
},
register::{
- CredProtect, CredentialCreationOptions, DisplayName, Extension as RegExt,
- FourToSixtyThree, PublicKeyCredentialUserEntity, RegistrationVerificationOptions,
- UserHandle,
+ CredProtect, CredentialCreationOptions, Extension as RegExt, FourToSixtyThree,
+ PublicKeyCredentialUserEntity, RegistrationVerificationOptions, UserHandle,
},
};
use super::{AsciiDomainStatic, Hints, PublicKeyCredentialHint};
@@ -1991,9 +1990,9 @@ mod tests {
let mut opts = CredentialCreationOptions::passkey(
RP_ID,
PublicKeyCredentialUserEntity {
- name: "foo".try_into()?,
+ name: "foo",
id: &id,
- display_name: DisplayName::Blank,
+ display_name: "",
},
Vec::new(),
);
@@ -2558,9 +2557,9 @@ mod tests {
let mut opts = CredentialCreationOptions::passkey(
RP_ID,
PublicKeyCredentialUserEntity {
- name: "foo".try_into()?,
+ name: "foo",
id: &id,
- display_name: DisplayName::Blank,
+ display_name: "",
},
Vec::new(),
);
@@ -5425,9 +5424,9 @@ mod tests {
let mut opts = CredentialCreationOptions::passkey(
RP_ID,
PublicKeyCredentialUserEntity {
- name: "foo".try_into()?,
+ name: "foo",
id: &id,
- display_name: DisplayName::Blank,
+ display_name: "",
},
Vec::new(),
);
@@ -7652,9 +7651,9 @@ mod tests {
let mut opts = CredentialCreationOptions::passkey(
RP_ID,
PublicKeyCredentialUserEntity {
- name: "foo".try_into()?,
+ name: "foo",
id: &id,
- display_name: DisplayName::Blank,
+ display_name: "",
},
Vec::new(),
);
@@ -9244,9 +9243,9 @@ mod tests {
let mut opts = CredentialCreationOptions::passkey(
RP_ID,
PublicKeyCredentialUserEntity {
- name: "foo".try_into()?,
+ name: "foo",
id: &id,
- display_name: DisplayName::Blank,
+ display_name: "",
},
Vec::new(),
);
@@ -9623,9 +9622,9 @@ mod tests {
let mut opts = CredentialCreationOptions::passkey(
RP_ID,
PublicKeyCredentialUserEntity {
- name: "foo".try_into()?,
+ name: "foo",
id: &id,
- display_name: DisplayName::Blank,
+ display_name: "",
},
Vec::new(),
);
@@ -10039,9 +10038,9 @@ mod tests {
let mut opts = CredentialCreationOptions::passkey(
RP_ID,
PublicKeyCredentialUserEntity {
- name: "foo".try_into()?,
+ name: "foo",
id: &id,
- display_name: DisplayName::Blank,
+ display_name: "",
},
Vec::new(),
);
diff --git a/src/request/register.rs b/src/request/register.rs
@@ -1,4 +1,3 @@
-extern crate alloc;
use super::{
super::{
DynamicState, Metadata, RegisteredCredential, StaticState,
@@ -15,7 +14,7 @@ use super::{
BackupReq, Ceremony, Challenge, CredentialMediationRequirement, ExtensionInfo, ExtensionReq,
FIVE_MINUTES, Hints, Origin, PrfInput, PublicKeyCredentialDescriptor, RpId, SentChallenge,
TimedCeremony, UserVerificationRequirement,
- register::error::{CreationOptionsErr, NicknameErr, UsernameErr},
+ register::error::CreationOptionsErr,
};
#[cfg(doc)]
use crate::{
@@ -25,7 +24,6 @@ use crate::{
},
response::{AuthTransports, AuthenticatorTransport, Backup, CollectedClientData, Flag},
};
-use alloc::borrow::Cow;
use core::{
borrow::Borrow,
cmp::Ordering,
@@ -35,7 +33,6 @@ use core::{
num::{NonZeroU32, NonZeroU64},
time::Duration,
};
-use precis_profiles::{UsernameCasePreserved, precis_core::profile::Profile as _};
#[cfg(any(doc, not(feature = "serializable_server_state")))]
use std::time::Instant;
#[cfg(any(doc, feature = "serializable_server_state"))]
@@ -190,500 +187,6 @@ impl Display for CredProtect {
}
}
}
-/// String returned from the [Nickname Enforcement rule](https://www.rfc-editor.org/rfc/rfc8266#section-2.3)
-/// as defined in RFC 8266.
-///
-/// Note [string truncation](https://www.w3.org/TR/webauthn-3/#sctn-strings-truncation) is allowed, so one may
-/// want to enforce [`Self::RECOMMENDED_MAX_LEN`].
-#[derive(Clone, Debug)]
-pub struct Nickname<'a>(Cow<'a, str>);
-impl<'a> Nickname<'a> {
- /// The maximum allowed length.
- pub const MAX_LEN: usize = 1023;
- /// The recommended maximum length to allow.
- pub const RECOMMENDED_MAX_LEN: usize = 64;
- /// Returns a `Nickname` that consumes `self`. When `self` owns the data, the data is simply moved;
- /// when the data is borrowed, then it is cloned into an owned instance.
- #[inline]
- #[must_use]
- pub fn into_owned<'b>(self) -> Nickname<'b> {
- Nickname(Cow::Owned(self.0.into_owned()))
- }
- /// Same as [`Self::with_max_len`] except the length must not exceed [`Self::RECOMMENDED_MAX_LEN`] instead of
- /// [`Self::MAX_LEN`].
- ///
- /// # Errors
- ///
- /// Errors iff `value` violates [RFC 8266 § 2.3](https://www.rfc-editor.org/rfc/rfc8266.html#section-2.3)
- /// or the resulting length exceeds [`Self::RECOMMENDED_MAX_LEN`].
- #[expect(single_use_lifetimes, reason = "false positive")]
- #[inline]
- pub fn with_recommended_len<'b: 'a>(value: Cow<'b, str>) -> Result<Self, NicknameErr> {
- precis_profiles::Nickname::new()
- .enforce(value)
- .map_err(|_e| NicknameErr::Rfc8266)
- .and_then(|val| {
- if val.len() <= Self::RECOMMENDED_MAX_LEN {
- Ok(Self(val))
- } else {
- Err(NicknameErr::Len)
- }
- })
- }
- /// Same as [`Self::try_from`].
- ///
- /// # Errors
- ///
- /// Errors iff `value` violates [RFC 8266 § 2.3](https://www.rfc-editor.org/rfc/rfc8266.html#section-2.3)
- /// or the resulting length exceeds [`Self::MAX_LEN`].
- #[expect(single_use_lifetimes, reason = "false positive")]
- #[inline]
- pub fn with_max_len<'b: 'a>(value: Cow<'b, str>) -> Result<Self, NicknameErr> {
- Self::try_from(value)
- }
-}
-impl AsRef<str> for Nickname<'_> {
- #[inline]
- fn as_ref(&self) -> &str {
- self.0.as_ref()
- }
-}
-impl Borrow<str> for Nickname<'_> {
- #[inline]
- fn borrow(&self) -> &str {
- self.0.as_ref()
- }
-}
-impl<'a: 'b, 'b> From<&'a Nickname<'_>> for Nickname<'b> {
- #[inline]
- fn from(value: &'a Nickname<'_>) -> Self {
- match value.0 {
- Cow::Borrowed(val) => Self(Cow::Borrowed(val)),
- Cow::Owned(ref val) => Self(Cow::Borrowed(val.as_str())),
- }
- }
-}
-impl<'a: 'b, 'b> From<Nickname<'a>> for Cow<'b, str> {
- #[inline]
- fn from(value: Nickname<'a>) -> Self {
- value.0
- }
-}
-impl<'a: 'b, 'b> TryFrom<Cow<'a, str>> for Nickname<'b> {
- type Error = NicknameErr;
- /// # Examples
- ///
- /// ```
- /// # use std::borrow::Cow;
- /// # use webauthn_rp::request::register::{error::NicknameErr, Nickname};
- /// assert_eq!(
- /// Nickname::try_from(Cow::Borrowed("Srinivasa Ramanujan"))?.as_ref(),
- /// "Srinivasa Ramanujan"
- /// );
- /// assert_eq!(
- /// Nickname::try_from(Cow::Borrowed("श्रीनिवास रामानुजन्"))?.as_ref(),
- /// "श्रीनिवास रामानुजन्"
- /// );
- /// // Empty strings are not valid.
- /// assert!(Nickname::try_from(Cow::Borrowed("")).map_or_else(
- /// |e| matches!(e, NicknameErr::Rfc8266),
- /// |_| false
- /// ));
- /// # Ok::<_, webauthn_rp::AggErr>(())
- /// ```
- #[inline]
- fn try_from(value: Cow<'a, str>) -> Result<Self, Self::Error> {
- precis_profiles::Nickname::new()
- .enforce(value)
- .map_err(|_e| NicknameErr::Rfc8266)
- .and_then(|val| {
- if val.len() <= Self::MAX_LEN {
- Ok(Self(val))
- } else {
- Err(NicknameErr::Len)
- }
- })
- }
-}
-impl<'a: 'b, 'b> TryFrom<&'a str> for Nickname<'b> {
- type Error = NicknameErr;
- /// Same as [`Nickname::try_from`] except the input is a `str`.
- #[inline]
- fn try_from(value: &'a str) -> Result<Self, Self::Error> {
- Self::try_from(Cow::Borrowed(value))
- }
-}
-impl TryFrom<String> for Nickname<'_> {
- type Error = NicknameErr;
- /// Same as [`Nickname::try_from`] except the input is a `String`.
- #[inline]
- fn try_from(value: String) -> Result<Self, Self::Error> {
- Self::try_from(Cow::Owned(value))
- }
-}
-impl PartialEq<Nickname<'_>> for Nickname<'_> {
- #[inline]
- fn eq(&self, other: &Nickname<'_>) -> bool {
- self.0 == other.0
- }
-}
-impl PartialEq<&Nickname<'_>> for Nickname<'_> {
- #[inline]
- fn eq(&self, other: &&Nickname<'_>) -> bool {
- *self == **other
- }
-}
-impl PartialEq<Nickname<'_>> for &Nickname<'_> {
- #[inline]
- fn eq(&self, other: &Nickname<'_>) -> bool {
- **self == *other
- }
-}
-impl Eq for Nickname<'_> {}
-impl Hash for Nickname<'_> {
- #[inline]
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.0.hash(state);
- }
-}
-impl PartialOrd<Nickname<'_>> for Nickname<'_> {
- #[inline]
- fn partial_cmp(&self, other: &Nickname<'_>) -> Option<Ordering> {
- self.0.partial_cmp(&other.0)
- }
-}
-impl Ord for Nickname<'_> {
- #[inline]
- fn cmp(&self, other: &Self) -> Ordering {
- self.0.cmp(&other.0)
- }
-}
-/// Name intended to be displayed to a user.
-#[derive(Clone, Debug)]
-pub enum DisplayName<'a> {
- /// A blank string.
- Blank,
- /// A non-blank string conforming to RFC 8266.
- Nickname(Nickname<'a>),
-}
-impl<'a> DisplayName<'a> {
- /// The maximum allowed length.
- pub const MAX_LEN: usize = 1023;
- /// The recommended maximum length to allow.
- pub const RECOMMENDED_MAX_LEN: usize = 64;
- /// Returns a `DisplayName` that consumes `self`. When `self` owns the data, the data is simply moved;
- /// when the data is borrowed, then it is cloned into an owned instance.
- #[inline]
- #[must_use]
- pub fn into_owned<'b>(self) -> DisplayName<'b> {
- match self {
- Self::Blank => DisplayName::Blank,
- Self::Nickname(val) => DisplayName::Nickname(val.into_owned()),
- }
- }
- /// Same as [`Self::with_max_len`] except the length must not exceed [`Self::RECOMMENDED_MAX_LEN`] instead of
- /// [`Self::MAX_LEN`].
- ///
- /// # Errors
- ///
- /// Errors iff `value` is not empty and [`Nickname::with_recommended_len`] errors.
- #[expect(single_use_lifetimes, reason = "false positive")]
- #[inline]
- pub fn with_recommended_len<'b: 'a>(value: Cow<'b, str>) -> Result<Self, NicknameErr> {
- if value.is_empty() {
- Ok(Self::Blank)
- } else {
- Nickname::with_recommended_len(value).map(Self::Nickname)
- }
- }
- /// Same as [`Self::try_from`].
- ///
- /// # Errors
- ///
- /// Errors iff `value` is not empty and [`Nickname::with_max_len`] errors.
- #[expect(single_use_lifetimes, reason = "false positive")]
- #[inline]
- pub fn with_max_len<'b: 'a>(value: Cow<'b, str>) -> Result<Self, NicknameErr> {
- Self::try_from(value)
- }
-}
-impl AsRef<str> for DisplayName<'_> {
- #[inline]
- fn as_ref(&self) -> &str {
- match *self {
- Self::Blank => "",
- Self::Nickname(ref val) => val.as_ref(),
- }
- }
-}
-impl Borrow<str> for DisplayName<'_> {
- #[inline]
- fn borrow(&self) -> &str {
- self.as_ref()
- }
-}
-impl<'a: 'b, 'b> From<&'a DisplayName<'_>> for DisplayName<'b> {
- #[inline]
- fn from(value: &'a DisplayName<'_>) -> Self {
- match *value {
- DisplayName::Blank => Self::Blank,
- DisplayName::Nickname(ref val) => Self::Nickname(val.into()),
- }
- }
-}
-impl<'a: 'b, 'b> From<DisplayName<'a>> for Cow<'b, str> {
- #[inline]
- fn from(value: DisplayName<'a>) -> Self {
- match value {
- DisplayName::Blank => Cow::Borrowed(""),
- DisplayName::Nickname(val) => val.into(),
- }
- }
-}
-impl<'a: 'b, 'b> TryFrom<Cow<'a, str>> for DisplayName<'b> {
- type Error = NicknameErr;
- /// # Examples
- ///
- /// ```
- /// # use std::borrow::Cow;
- /// # use webauthn_rp::request::register::{error::NicknameErr, DisplayName};
- /// assert_eq!(
- /// DisplayName::try_from(Cow::Borrowed(""))?.as_ref(),
- /// ""
- /// );
- /// assert_eq!(
- /// DisplayName::try_from(Cow::Borrowed("Sir Isaac Newton"))?.as_ref(),
- /// "Sir Isaac Newton"
- /// );
- /// # Ok::<_, NicknameErr>(())
- /// ```
- #[inline]
- fn try_from(value: Cow<'a, str>) -> Result<Self, Self::Error> {
- if value.is_empty() {
- Ok(Self::Blank)
- } else {
- Nickname::try_from(value).map(Self::Nickname)
- }
- }
-}
-impl<'a: 'b, 'b> TryFrom<&'a str> for DisplayName<'b> {
- type Error = NicknameErr;
- /// Same as [`DisplayName::try_from`] except the input is a `str`.
- #[inline]
- fn try_from(value: &'a str) -> Result<Self, Self::Error> {
- Self::try_from(Cow::Borrowed(value))
- }
-}
-impl TryFrom<String> for DisplayName<'_> {
- type Error = NicknameErr;
- /// Same as [`DisplayName::try_from`] except the input is a `String`.
- #[inline]
- fn try_from(value: String) -> Result<Self, Self::Error> {
- Self::try_from(Cow::Owned(value))
- }
-}
-impl PartialEq<DisplayName<'_>> for DisplayName<'_> {
- #[inline]
- fn eq(&self, other: &DisplayName<'_>) -> bool {
- self.as_ref() == other.as_ref()
- }
-}
-impl PartialEq<&DisplayName<'_>> for DisplayName<'_> {
- #[inline]
- fn eq(&self, other: &&DisplayName<'_>) -> bool {
- *self == **other
- }
-}
-impl PartialEq<DisplayName<'_>> for &DisplayName<'_> {
- #[inline]
- fn eq(&self, other: &DisplayName<'_>) -> bool {
- **self == *other
- }
-}
-impl Eq for DisplayName<'_> {}
-impl Hash for DisplayName<'_> {
- #[inline]
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.as_ref().hash(state);
- }
-}
-impl PartialOrd<DisplayName<'_>> for DisplayName<'_> {
- #[inline]
- fn partial_cmp(&self, other: &DisplayName<'_>) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-impl Ord for DisplayName<'_> {
- #[inline]
- fn cmp(&self, other: &Self) -> Ordering {
- self.as_ref().cmp(other.as_ref())
- }
-}
-/// String returned from the
-/// [UsernameCasePreserved Enforcement rule](https://www.rfc-editor.org/rfc/rfc8265#section-3.4.3) as defined in
-/// RFC 8265.
-///
-/// Note [string truncation](https://www.w3.org/TR/webauthn-3/#sctn-strings-truncation) is allowed, so one may
-/// want to enforce [`Self::RECOMMENDED_MAX_LEN`].
-#[derive(Clone, Debug)]
-pub struct Username<'a>(Cow<'a, str>);
-impl<'a> Username<'a> {
- /// The maximum allowed length.
- pub const MAX_LEN: usize = 1023;
- /// The recommended maximum length to allow.
- pub const RECOMMENDED_MAX_LEN: usize = 64;
- /// Returns a `Username` that consumes `self`. When `self` owns the data, the data is simply moved;
- /// when the data is borrowed, then it is cloned into an owned instance.
- #[inline]
- #[must_use]
- pub fn into_owned<'b>(self) -> Username<'b> {
- Username(Cow::Owned(self.0.into_owned()))
- }
- /// Same as [`Self::with_max_len`] except the length must not exceed [`Self::RECOMMENDED_MAX_LEN`] instead of
- /// [`Self::MAX_LEN`].
- ///
- /// # Errors
- ///
- /// Errors iff `value` violates [RFC 8265 § 3.4.3](https://www.rfc-editor.org/rfc/rfc8265.html#section-3.4.3)
- /// or the resulting length exceeds [`Self::RECOMMENDED_MAX_LEN`].
- #[expect(single_use_lifetimes, reason = "false positive")]
- #[inline]
- pub fn with_recommended_len<'b: 'a>(value: Cow<'b, str>) -> Result<Self, UsernameErr> {
- UsernameCasePreserved::default()
- .enforce(value)
- .map_err(|_e| UsernameErr::Rfc8265)
- .and_then(|val| {
- if val.len() <= Self::RECOMMENDED_MAX_LEN {
- Ok(Self(val))
- } else {
- Err(UsernameErr::Len)
- }
- })
- }
- /// Same as [`Self::try_from`].
- ///
- /// # Errors
- ///
- /// Errors iff `value` violates [RFC 8265 § 3.4.3](https://www.rfc-editor.org/rfc/rfc8265.html#section-3.4.3)
- /// or the resulting length exceeds [`Self::MAX_LEN`].
- #[expect(single_use_lifetimes, reason = "false positive")]
- #[inline]
- pub fn with_max_len<'b: 'a>(value: Cow<'b, str>) -> Result<Self, UsernameErr> {
- Self::try_from(value)
- }
-}
-impl AsRef<str> for Username<'_> {
- #[inline]
- fn as_ref(&self) -> &str {
- self.0.as_ref()
- }
-}
-impl Borrow<str> for Username<'_> {
- #[inline]
- fn borrow(&self) -> &str {
- self.0.as_ref()
- }
-}
-impl<'a: 'b, 'b> TryFrom<Cow<'a, str>> for Username<'b> {
- type Error = UsernameErr;
- /// # Examples
- ///
- /// ```
- /// # use std::borrow::Cow;
- /// # use webauthn_rp::request::register::{error::UsernameErr, Username};
- /// assert_eq!(
- /// Username::try_from(Cow::Borrowed("leonhard.euler"))?.as_ref(),
- /// "leonhard.euler"
- /// );
- /// // Empty strings are not valid.
- /// assert!(Username::try_from(Cow::Borrowed("")).map_or_else(
- /// |e| matches!(e, UsernameErr::Rfc8265),
- /// |_| false
- /// ));
- /// # Ok::<_, webauthn_rp::AggErr>(())
- /// ```
- #[inline]
- fn try_from(value: Cow<'a, str>) -> Result<Self, Self::Error> {
- UsernameCasePreserved::default()
- .enforce(value)
- .map_err(|_e| UsernameErr::Rfc8265)
- .and_then(|val| {
- if val.len() <= Self::MAX_LEN {
- Ok(Self(val))
- } else {
- Err(UsernameErr::Len)
- }
- })
- }
-}
-impl<'a: 'b, 'b> TryFrom<&'a str> for Username<'b> {
- type Error = UsernameErr;
- /// Same as [`Username::try_from`] except the input is a `str`.
- #[inline]
- fn try_from(value: &'a str) -> Result<Self, Self::Error> {
- Self::try_from(Cow::Borrowed(value))
- }
-}
-impl TryFrom<String> for Username<'_> {
- type Error = UsernameErr;
- /// Same as [`Username::try_from`] except the input is a `String`.
- #[inline]
- fn try_from(value: String) -> Result<Self, Self::Error> {
- Self::try_from(Cow::Owned(value))
- }
-}
-impl<'a: 'b, 'b> From<Username<'a>> for Cow<'b, str> {
- #[inline]
- fn from(value: Username<'a>) -> Self {
- value.0
- }
-}
-impl<'a: 'b, 'b> From<&'a Username<'_>> for Username<'b> {
- #[inline]
- fn from(value: &'a Username<'_>) -> Self {
- match value.0 {
- Cow::Borrowed(val) => Self(Cow::Borrowed(val)),
- Cow::Owned(ref val) => Self(Cow::Borrowed(val.as_str())),
- }
- }
-}
-impl PartialEq<Username<'_>> for Username<'_> {
- #[inline]
- fn eq(&self, other: &Username<'_>) -> bool {
- self.0 == other.0
- }
-}
-impl PartialEq<&Username<'_>> for Username<'_> {
- #[inline]
- fn eq(&self, other: &&Username<'_>) -> bool {
- *self == **other
- }
-}
-impl PartialEq<Username<'_>> for &Username<'_> {
- #[inline]
- fn eq(&self, other: &Username<'_>) -> bool {
- **self == *other
- }
-}
-impl Eq for Username<'_> {}
-impl Hash for Username<'_> {
- #[inline]
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.0.hash(state);
- }
-}
-impl PartialOrd<Username<'_>> for Username<'_> {
- #[inline]
- fn partial_cmp(&self, other: &Username<'_>) -> Option<Ordering> {
- self.0.partial_cmp(&other.0)
- }
-}
-impl Ord for Username<'_> {
- #[inline]
- fn cmp(&self, other: &Self) -> Ordering {
- self.0.cmp(&other.0)
- }
-}
/// [`COSEAlgorithmIdentifier`](https://www.w3.org/TR/webauthn-3/#typedefdef-cosealgorithmidentifier).
///
/// Note the order of variants is the following:
@@ -1292,11 +795,17 @@ impl UserHandle16 {
#[derive(Clone, Debug)]
pub struct PublicKeyCredentialUserEntity<'name, 'display_name, 'id, const LEN: usize> {
/// [`name`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialentity-name).
- pub name: Username<'name>,
+ ///
+ /// Note the spec recommends RPs enforce
+ /// [RFC 8265 § 3.4.3](https://www.rfc-editor.org/rfc/rfc8265#section-3.4.3).
+ pub name: &'name str,
/// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentity-id).
pub id: &'id UserHandle<LEN>,
/// [`displayName`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentity-displayname).
- pub display_name: DisplayName<'display_name>,
+ ///
+ /// Note the spec recommends RPs enforce [RFC 8266 § 2.3](https://www.rfc-editor.org/rfc/rfc8266#section-2.3)
+ /// when this isn't empty.
+ pub display_name: &'display_name str,
}
/// `PublicKeyCredentialUserEntity` based on a [`UserHandle64`].
pub type PublicKeyCredentialUserEntity64<'name, 'display_name, 'id> =
@@ -1576,9 +1085,9 @@ impl<
/// CredentialCreationOptions::passkey(
/// &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
/// PublicKeyCredentialUserEntity {
- /// name: "bernard.riemann".try_into()?,
+ /// name: "bernard.riemann",
/// id: &UserHandle64::new(),
- /// display_name: "Georg Friedrich Bernhard Riemann".try_into()?,
+ /// display_name: "Georg Friedrich Bernhard Riemann",
/// },
/// Vec::new()
/// ).start_ceremony()?.0.expiration() > Instant::now()
@@ -1766,9 +1275,9 @@ impl<'rp_id, 'user_name, 'user_display_name, 'user_id, const USER_LEN: usize>
/// PublicKeyCredentialCreationOptions::passkey(
/// &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
/// PublicKeyCredentialUserEntity {
- /// name: "archimedes.of.syracuse".try_into()?,
+ /// name: "archimedes.of.syracuse",
/// id: &UserHandle64::new(),
- /// display_name: "Αρχιμήδης ο Συρακούσιος".try_into()?,
+ /// display_name: "Αρχιμήδης ο Συρακούσιος",
/// },
/// Vec::new()
/// )
@@ -1837,9 +1346,9 @@ impl<'rp_id, 'user_name, 'user_display_name, 'user_id, const USER_LEN: usize>
/// PublicKeyCredentialCreationOptions::second_factor(
/// &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
/// PublicKeyCredentialUserEntity {
- /// name: "carl.gauss".try_into()?,
+ /// name: "carl.gauss",
/// id: &UserHandle64::new(),
- /// display_name: "Johann Carl Friedrich Gauß".try_into()?,
+ /// display_name: "Johann Carl Friedrich Gauß",
/// },
/// Vec::new()
/// )
@@ -1955,9 +1464,9 @@ impl<
/// CredentialCreationOptions::passkey(
/// &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
/// PublicKeyCredentialUserEntity {
- /// name: "david.hilbert".try_into()?,
+ /// name: "david.hilbert",
/// id: &UserHandle64::new(),
- /// display_name: "David Hilbert".try_into()?,
+ /// display_name: "David Hilbert",
/// },
/// Vec::new()
/// )
@@ -2622,7 +2131,7 @@ mod tests {
))]
use super::{
super::{super::AggErr, ExtensionInfo},
- Challenge, CredProtect, CredentialCreationOptions, DisplayName, FourToSixtyThree, PrfInput,
+ Challenge, CredProtect, CredentialCreationOptions, FourToSixtyThree, PrfInput,
PublicKeyCredentialUserEntity, RpId, UserHandle,
};
#[cfg(all(feature = "custom", feature = "serializable_server_state"))]
@@ -2647,7 +2156,7 @@ mod tests {
AuthTransports,
},
AuthenticatorAttachment, BackupReq, ExtensionErr, ExtensionReq, RegCeremonyErr,
- Registration, RegistrationVerificationOptions, UserVerificationRequirement, Username,
+ Registration, RegistrationVerificationOptions, UserVerificationRequirement,
};
#[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
use rsa::sha2::{Digest as _, Sha256};
@@ -2676,9 +2185,9 @@ mod tests {
let mut opts = CredentialCreationOptions::passkey(
&rp_id,
PublicKeyCredentialUserEntity {
- name: "foo".try_into()?,
+ name: "foo",
id: &id,
- display_name: DisplayName::Blank,
+ display_name: "",
},
Vec::new(),
);
@@ -3089,7 +2598,6 @@ mod tests {
}
attestation_object
}
- #[expect(clippy::unwrap_in_result, clippy::unwrap_used, reason = "OK in tests")]
#[cfg(all(feature = "custom", not(any(feature = "bin", feature = "serde"))))]
fn validate(options: TestOptions) -> Result<(), AggErr> {
let rp_id = RpId::Domain("example.com".to_owned().try_into()?);
@@ -3125,8 +2633,8 @@ mod tests {
&rp_id,
PublicKeyCredentialUserEntity {
id: &user,
- name: Username::try_from("blank").unwrap(),
- display_name: DisplayName::Blank,
+ name: "",
+ display_name: "",
},
Vec::new(),
);
diff --git a/src/request/register/bin.rs b/src/request/register/bin.rs
@@ -1,14 +1,8 @@
-extern crate alloc;
use super::{
super::super::bin::{Decode, Encode},
- DisplayName, Nickname, NicknameErr, UserHandle, Username, UsernameErr,
-};
-use alloc::borrow::Cow;
-use core::{
- convert::Infallible,
- error::Error,
- fmt::{self, Display, Formatter},
+ UserHandle,
};
+use core::convert::Infallible;
impl<const LEN: usize> Encode for UserHandle<LEN> {
type Output<'a>
= [u8; LEN]
@@ -31,108 +25,3 @@ where
Ok(Self(input))
}
}
-impl Encode for DisplayName<'_> {
- type Output<'a>
- = &'a str
- where
- Self: 'a;
- type Err = Infallible;
- #[inline]
- fn encode(&self) -> Result<Self::Output<'_>, Self::Err> {
- Ok(self.as_ref())
- }
-}
-/// Error returned from [`DisplayName::decode`].
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum DecodeDisplayNameErr {
- /// Variant returned when the encoded data could not be decoded
- /// into a [`DisplayName`].
- Nickname(NicknameErr),
- /// Variant returned when the [`DisplayName`] was not encoded
- /// into its canonical form.
- NotCanonical,
-}
-impl Display for DecodeDisplayNameErr {
- #[inline]
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- match *self {
- Self::Nickname(e) => e.fmt(f),
- Self::NotCanonical => f.write_str("DisplayName was not encoded in its canonical form"),
- }
- }
-}
-impl Error for DecodeDisplayNameErr {}
-impl<'b> Decode for DisplayName<'b> {
- type Input<'a> = &'b str;
- type Err = DecodeDisplayNameErr;
- #[inline]
- fn decode(input: Self::Input<'_>) -> Result<Self, Self::Err> {
- match DisplayName::try_from(input).map_err(DecodeDisplayNameErr::Nickname) {
- Ok(v) => match v {
- DisplayName::Blank => Ok(Self::Blank),
- DisplayName::Nickname(name) => match name.0 {
- Cow::Borrowed(val) => {
- if val == input {
- Ok(Self::Nickname(Nickname(Cow::Borrowed(input))))
- } else {
- Err(DecodeDisplayNameErr::NotCanonical)
- }
- }
- Cow::Owned(_) => Err(DecodeDisplayNameErr::NotCanonical),
- },
- },
- Err(e) => Err(e),
- }
- }
-}
-impl Encode for Username<'_> {
- type Output<'a>
- = &'a str
- where
- Self: 'a;
- type Err = Infallible;
- #[inline]
- fn encode(&self) -> Result<Self::Output<'_>, Self::Err> {
- Ok(self.as_ref())
- }
-}
-/// Error returned from [`Username::decode`].
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum DecodeUsernameErr {
- /// Variant returned when the encoded data could not be decoded
- /// into a [`Username`].
- Username(UsernameErr),
- /// Variant returned when the [`Username`] was not encoded
- /// into its canonical form.
- NotCanonical,
-}
-impl Display for DecodeUsernameErr {
- #[inline]
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- match *self {
- Self::Username(e) => e.fmt(f),
- Self::NotCanonical => f.write_str("Username was not encoded in its canonical form"),
- }
- }
-}
-impl Error for DecodeUsernameErr {}
-impl<'b> Decode for Username<'b> {
- type Input<'a> = &'b str;
- type Err = DecodeUsernameErr;
- #[inline]
- fn decode(input: Self::Input<'_>) -> Result<Self, Self::Err> {
- match Username::try_from(input).map_err(DecodeUsernameErr::Username) {
- Ok(v) => match v.0 {
- Cow::Borrowed(name) => {
- if name == input {
- Ok(Self(Cow::Borrowed(input)))
- } else {
- Err(DecodeUsernameErr::NotCanonical)
- }
- }
- Cow::Owned(_) => Err(DecodeUsernameErr::NotCanonical),
- },
- Err(e) => Err(e),
- }
- }
-}
diff --git a/src/request/register/error.rs b/src/request/register/error.rs
@@ -1,8 +1,8 @@
#[cfg(doc)]
use super::{
- AuthenticatorSelectionCriteria, CredProtect, CredentialCreationOptions, Extension, Nickname,
+ AuthenticatorSelectionCriteria, CredProtect, CredentialCreationOptions, Extension,
PublicKeyCredentialCreationOptions, USER_HANDLE_MAX_LEN, USER_HANDLE_MIN_LEN, UserHandle,
- UserVerificationRequirement, Username,
+ UserVerificationRequirement,
};
use core::{
error::Error,
@@ -10,46 +10,6 @@ use core::{
};
#[cfg(doc)]
use std::time::{Instant, SystemTime};
-/// Error returned by [`Nickname::try_from`].
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum NicknameErr {
- /// Error returned when the [Nickname Enforcement rule](https://www.rfc-editor.org/rfc/rfc8266#section-2.3)
- /// fails.
- Rfc8266,
- /// Error returned when the length of the transformed string would exceed [`Nickname::MAX_LEN`] or
- /// [`Nickname::RECOMMENDED_MAX_LEN`].
- Len,
-}
-impl Display for NicknameErr {
- #[inline]
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- f.write_str(match *self {
- Self::Rfc8266 => "nickname does not conform to RFC 8266",
- Self::Len => "length of nickname is too long",
- })
- }
-}
-impl Error for NicknameErr {}
-/// Error returned by [`Username::try_from`].
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum UsernameErr {
- /// Error returned when the
- /// [UsernameCasePreserved Enforcement rule](https://www.rfc-editor.org/rfc/rfc8265#section-3.4.3) fails.
- Rfc8265,
- /// Error returned when the length of the transformed string would exceed [`Username::MAX_LEN`] or
- /// [`Username::RECOMMENDED_MAX_LEN`].
- Len,
-}
-impl Display for UsernameErr {
- #[inline]
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- f.write_str(match *self {
- Self::Rfc8265 => "username does not conform to RFC 8265",
- Self::Len => "length of username is too long",
- })
- }
-}
-impl Error for UsernameErr {}
/// Error returned by [`CredentialCreationOptions::start_ceremony`].
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CreationOptionsErr {
diff --git a/src/request/register/ser.rs b/src/request/register/ser.rs
@@ -1,4 +1,3 @@
-extern crate alloc;
use super::{
super::{
super::response::ser::{Null, Type},
@@ -7,19 +6,17 @@ use super::{
},
AuthenticatorAttachment, AuthenticatorSelectionCriteria, Challenge, CoseAlgorithmIdentifier,
CoseAlgorithmIdentifiers, CredProtect, CredentialCreationOptions,
- CredentialMediationRequirement, DisplayName, Extension, ExtensionInfo, ExtensionReq,
- FIVE_MINUTES, FourToSixtyThree, Hints, Nickname, PrfInput, PublicKeyCredentialCreationOptions,
+ CredentialMediationRequirement, Extension, ExtensionInfo, ExtensionReq, FIVE_MINUTES,
+ FourToSixtyThree, Hints, PrfInput, PublicKeyCredentialCreationOptions,
PublicKeyCredentialDescriptor, PublicKeyCredentialUserEntity, RegistrationClientState,
- ResidentKeyRequirement, RpId, UserHandle, UserVerificationRequirement, Username,
+ ResidentKeyRequirement, RpId, UserHandle, UserVerificationRequirement,
};
-use alloc::borrow::Cow;
#[cfg(doc)]
use core::str::FromStr;
use core::{
convert,
error::Error as E,
fmt::{self, Display, Formatter},
- marker::PhantomData,
num::NonZeroU32,
str,
};
@@ -27,69 +24,6 @@ use serde::{
de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Unexpected, Visitor},
ser::{Serialize, SerializeSeq as _, SerializeStruct as _, Serializer},
};
-impl Serialize for Nickname<'_> {
- /// Serializes `self` as a [`prim@str`].
- ///
- /// # Examples
- ///
- /// ```
- /// # use webauthn_rp::request::register::Nickname;
- /// assert_eq!(
- /// serde_json::to_string(&Nickname::try_from("Giuseppe Luigi Lagrangia")?).unwrap(),
- /// r#""Giuseppe Luigi Lagrangia""#
- /// );
- /// # Ok::<_, webauthn_rp::AggErr>(())
- /// ```
- #[inline]
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- serializer.serialize_str(self.0.as_ref())
- }
-}
-impl Serialize for DisplayName<'_> {
- /// Serializes `self` as a [`prim@str`].
- ///
- /// # Examples
- ///
- /// ```
- /// # use webauthn_rp::request::register::DisplayName;
- /// assert_eq!(
- /// serde_json::to_string(&DisplayName::try_from("Terence Tao")?).unwrap(),
- /// r#""Terence Tao""#
- /// );
- /// # Ok::<_, webauthn_rp::AggErr>(())
- /// ```
- #[inline]
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- serializer.serialize_str(self.as_ref())
- }
-}
-impl Serialize for Username<'_> {
- /// Serializes `self` as a [`prim@str`].
- ///
- /// # Examples
- ///
- /// ```
- /// # use webauthn_rp::request::register::Username;
- /// assert_eq!(
- /// serde_json::to_string(&Username::try_from("john.von.neumann")?).unwrap(),
- /// r#""john.von.neumann""#
- /// );
- /// # Ok::<_, webauthn_rp::AggErr>(())
- /// ```
- #[inline]
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: Serializer,
- {
- serializer.serialize_str(self.0.as_ref())
- }
-}
/// `"type"`
const TYPE: &str = "type";
/// `"public-key"`
@@ -316,7 +250,7 @@ where
/// # Examples
///
/// ```
- /// # use webauthn_rp::request::register::{DisplayName, PublicKeyCredentialUserEntity, UserHandle};
+ /// # use webauthn_rp::request::register::{PublicKeyCredentialUserEntity, UserHandle};
/// # #[cfg(feature = "custom")]
/// // We create this manually purely for example. One should almost always
/// // randomly generate this (e.g., `UserHandle::new`).
@@ -324,9 +258,9 @@ where
/// # #[cfg(feature = "custom")]
/// assert_eq!(
/// serde_json::to_string(&PublicKeyCredentialUserEntity {
- /// name: "georg.cantor".try_into()?,
+ /// name: "georg.cantor",
/// id: &id,
- /// display_name: "Гео́рг Ка́нтор".try_into()?,
+ /// display_name: "Гео́рг Ка́нтор",
/// }).unwrap(),
/// r#"{"name":"georg.cantor","id":"AA","displayName":"Гео́рг Ка́нтор"}"#
/// );
@@ -335,9 +269,9 @@ where
/// # #[cfg(feature = "custom")]
/// assert_eq!(
/// serde_json::to_string(&PublicKeyCredentialUserEntity {
- /// name: "georg.cantor".try_into()?,
+ /// name: "georg.cantor",
/// id: &id,
- /// display_name: DisplayName::Blank,
+ /// display_name: "",
/// }).unwrap(),
/// r#"{"name":"georg.cantor","id":"AA","displayName":""}"#
/// );
@@ -829,7 +763,7 @@ where
/// creds.push(PublicKeyCredentialDescriptor { id, transports });
/// let rp_id = RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?);
/// let user_handle = UserHandle64::new();
- /// let mut options = CredentialCreationOptions::passkey(&rp_id, PublicKeyCredentialUserEntity { name: "pierre.de.fermat".try_into()?, id: &user_handle, display_name: "Pierre de Fermat".try_into()?, }, creds);
+ /// let mut options = CredentialCreationOptions::passkey(&rp_id, PublicKeyCredentialUserEntity { name: "pierre.de.fermat", id: &user_handle, display_name: "Pierre de Fermat", }, creds);
/// options.public_key.authenticator_selection.authenticator_attachment = AuthenticatorAttachment::None;
/// options.public_key.hints = Hints::EMPTY.add(PublicKeyCredentialHint::SecurityKey);
/// options.public_key.extensions.min_pin_length = Some((FourToSixtyThree::Sixteen, ExtensionInfo::RequireEnforceValue));
@@ -924,139 +858,6 @@ where
self.0.serialize(serializer)
}
}
-impl<'de: 'a, 'a> Deserialize<'de> for Nickname<'a> {
- /// Deserializes [`prim@str`] and parses it according to [`Self::try_from`].
- ///
- /// # Examples
- ///
- /// ```
- /// # use webauthn_rp::request::register::Nickname;
- /// assert_eq!(
- /// serde_json::from_str::<Nickname>(r#""Henri Poincaré""#)?.as_ref(),
- /// "Henri Poincaré"
- /// );
- /// # Ok::<_, serde_json::Error>(())
- ///```
- #[inline]
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- /// `Visitor` for `Nickname`.
- struct NicknameVisitor<'b>(PhantomData<fn() -> &'b ()>);
- impl<'d: 'b, 'b> Visitor<'d> for NicknameVisitor<'b> {
- type Value = Nickname<'b>;
- fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
- formatter.write_str("Nickname")
- }
- fn visit_borrowed_str<E>(self, v: &'d str) -> Result<Self::Value, E>
- where
- E: Error,
- {
- Nickname::try_from(v).map_err(E::custom)
- }
- fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
- where
- E: Error,
- {
- Nickname::try_from(v)
- .map_err(E::custom)
- .map(|name| Nickname(Cow::Owned(name.0.into_owned())))
- }
- }
- deserializer.deserialize_str(NicknameVisitor(PhantomData))
- }
-}
-impl<'de: 'a, 'a> Deserialize<'de> for DisplayName<'a> {
- /// Deserializes [`prim@str`] and parses it according to [`Self::try_from`].
- ///
- /// # Examples
- ///
- /// ```
- /// # use webauthn_rp::request::register::DisplayName;
- /// assert_eq!(
- /// serde_json::from_str::<DisplayName>(r#""Alexander Grothendieck""#)?.as_ref(),
- /// "Alexander Grothendieck"
- /// );
- /// # Ok::<_, serde_json::Error>(())
- ///```
- #[inline]
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- /// `Visitor` for `DisplayName`.
- struct DisplayNameVisitor<'b>(PhantomData<fn() -> &'b ()>);
- impl<'d: 'b, 'b> Visitor<'d> for DisplayNameVisitor<'b> {
- type Value = DisplayName<'b>;
- fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
- formatter.write_str("DisplayName")
- }
- fn visit_borrowed_str<E>(self, v: &'d str) -> Result<Self::Value, E>
- where
- E: Error,
- {
- DisplayName::try_from(v).map_err(E::custom)
- }
- fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
- where
- E: Error,
- {
- if v.is_empty() {
- Ok(DisplayName::Blank)
- } else {
- Nickname::try_from(v).map_err(E::custom).map(|name| {
- DisplayName::Nickname(Nickname(Cow::Owned(name.0.into_owned())))
- })
- }
- }
- }
- deserializer.deserialize_str(DisplayNameVisitor(PhantomData))
- }
-}
-impl<'de: 'a, 'a> Deserialize<'de> for Username<'a> {
- /// Deserializes [`prim@str`] and parses it according to [`Self::try_from`].
- ///
- /// # Examples
- ///
- /// ```
- /// # use webauthn_rp::request::register::Username;
- /// assert_eq!(
- /// serde_json::from_str::<Username>(r#""augustin.cauchy""#)?.as_ref(),
- /// "augustin.cauchy"
- /// );
- /// # Ok::<_, serde_json::Error>(())
- ///```
- #[inline]
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- /// `Visitor` for `Username`.
- struct UsernameVisitor<'b>(PhantomData<fn() -> &'b ()>);
- impl<'d: 'b, 'b> Visitor<'d> for UsernameVisitor<'b> {
- type Value = Username<'b>;
- fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
- formatter.write_str("Username")
- }
- fn visit_borrowed_str<E>(self, v: &'d str) -> Result<Self::Value, E>
- where
- E: Error,
- {
- Username::try_from(v).map_err(E::custom)
- }
- fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
- where
- E: Error,
- {
- Username::try_from(v)
- .map_err(E::custom)
- .map(|name| Username(Cow::Owned(name.0.into_owned())))
- }
- }
- deserializer.deserialize_str(UsernameVisitor(PhantomData))
- }
-}
impl<'de, const LEN: usize> Deserialize<'de> for UserHandle<LEN>
where
Self: Default,
@@ -1178,7 +979,7 @@ impl<'de> Deserialize<'de> for PublicKeyCredentialRpEntityHelper {
/// ```json
/// {
/// "id": null | <RpId>,
- /// "name": null | "" | <RpId> | <Nickname>
+ /// "name": null | <RpId> | <Nickname>
/// }
/// ```
///
@@ -1246,19 +1047,11 @@ impl<'de> Deserialize<'de> for PublicKeyCredentialRpEntityHelper {
fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
formatter.write_str("RpId name")
}
- fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+ fn visit_str<E>(self, _: &str) -> Result<Self::Value, E>
where
E: Error,
{
- if v.is_empty() {
- Ok(Name)
- } else {
- Nickname::try_from(v).map(|_n| Name).or_else(|_e| {
- RpId::try_from(v.to_owned())
- .map_err(E::custom)
- .map(|_r| Name)
- })
- }
+ Ok(Name)
}
}
deserializer.deserialize_str(NameVisitor)
@@ -1299,13 +1092,13 @@ impl<'de> Deserialize<'de> for PublicKeyCredentialRpEntityHelper {
///
/// This is primarily useful to assist [`ClientCredentialCreationOptions::deserialize`].
#[derive(Debug, Default)]
-pub struct PublicKeyCredentialUserEntityOwned<'name, 'display_name, const LEN: usize> {
+pub struct PublicKeyCredentialUserEntityOwned<const LEN: usize> {
/// See [`PublicKeyCredentialUserEntity::name`].
- pub name: Option<Username<'name>>,
+ pub name: Option<String>,
/// See [`PublicKeyCredentialUserEntity::id`].
pub id: Option<UserHandle<LEN>>,
/// See [`PublicKeyCredentialUserEntity::display_name`].
- pub display_name: Option<DisplayName<'display_name>>,
+ pub display_name: Option<String>,
}
/// Error returned when converting a [`PublicKeyCredentialUserEntityOwned`] into a
/// [`PublicKeyCredentialUserEntity`] (e.g., via [`PublicKeyCredentialUserEntityOwned::with_id`]).
@@ -1329,7 +1122,7 @@ impl Display for PublicKeyCredentialUserEntityOwnedErr {
}
}
impl E for PublicKeyCredentialUserEntityOwnedErr {}
-impl<const LEN: usize> PublicKeyCredentialUserEntityOwned<'_, '_, LEN> {
+impl<const LEN: usize> PublicKeyCredentialUserEntityOwned<LEN> {
/// Returns a `PublicKeyCredentialUserEntity` based on `self`.
///
/// # Errors
@@ -1352,9 +1145,9 @@ impl<const LEN: usize> PublicKeyCredentialUserEntityOwned<'_, '_, LEN> {
.as_ref()
.ok_or(PublicKeyCredentialUserEntityOwnedErr::MissingDisplayName)
.map(|display| PublicKeyCredentialUserEntity {
- name: username.into(),
+ name: username,
id,
- display_name: display.into(),
+ display_name: display,
})
})
})
@@ -1382,9 +1175,9 @@ impl<const LEN: usize> PublicKeyCredentialUserEntityOwned<'_, '_, LEN> {
.as_ref()
.ok_or(PublicKeyCredentialUserEntityOwnedErr::MissingDisplayName)
.map(|display| PublicKeyCredentialUserEntity {
- name: username.into(),
+ name: username,
id,
- display_name: display.into(),
+ display_name: display,
})
})
}
@@ -1399,8 +1192,8 @@ impl<const LEN: usize> PublicKeyCredentialUserEntityOwned<'_, '_, LEN> {
#[inline]
pub fn with_name_and_display_name<'name, 'display_name>(
&self,
- name: Username<'name>,
- display_name: DisplayName<'display_name>,
+ name: &'name str,
+ display_name: &'display_name str,
) -> Result<
PublicKeyCredentialUserEntity<'name, 'display_name, '_, LEN>,
PublicKeyCredentialUserEntityOwnedErr,
@@ -1415,8 +1208,7 @@ impl<const LEN: usize> PublicKeyCredentialUserEntityOwned<'_, '_, LEN> {
})
}
}
-impl<'de: 'name + 'display_name, 'name, 'display_name, const LEN: usize> Deserialize<'de>
- for PublicKeyCredentialUserEntityOwned<'name, 'display_name, LEN>
+impl<'de, const LEN: usize> Deserialize<'de> for PublicKeyCredentialUserEntityOwned<LEN>
where
UserHandle<LEN>: Deserialize<'de>,
{
@@ -1426,10 +1218,10 @@ where
/// Note none of the fields are required and all of them are allowed to be `null`.
/// [`id`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentityjson-id) is deserialized
/// according to [`UserHandle::deserialize`],
- /// [`name`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentityjson-name) is deserialized
- /// according to [`Username::deserialize`], and
- /// [`displayName`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentityjson-displayname) is
- /// deserialized according to [`DisplayName::deserialize`].
+ /// [`name`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentityjson-name) deserializes
+ /// [`prim@str`].
+ /// [`displayName`](https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialuserentityjson-displayname)
+ /// deserializes [`prim@str`].
///
/// Unknown or duplicate fields lead to an error. Missing fields are interpreted the same as if the field
/// were assigned `null`.
@@ -1438,9 +1230,9 @@ where
///
/// ```
/// # use webauthn_rp::request::register::ser::PublicKeyCredentialUserEntityOwned;
- /// let val = serde_json::from_str::<PublicKeyCredentialUserEntityOwned<'_, '_, 16>>(r#"{"name":"paul.erdos","displayName":"Erdős Pál"}"#)?;
- /// assert!(val.name.is_some_and(|name| name.as_ref() == "paul.erdos"));
- /// assert!(val.display_name.is_some_and(|display| display.as_ref() == "Erdős Pál"));
+ /// let val = serde_json::from_str::<PublicKeyCredentialUserEntityOwned<16>>(r#"{"name":"paul.erdos","displayName":"Erdős Pál"}"#)?;
+ /// assert!(val.name.is_some_and(|name| name == "paul.erdos"));
+ /// assert!(val.display_name.is_some_and(|display| display == "Erdős Pál"));
/// assert!(val.id.is_none());
/// # Ok::<_, serde_json::Error>(())
/// ```
@@ -1450,15 +1242,12 @@ where
D: Deserializer<'de>,
{
/// `Visitor` for `PublicKeyCredentialUserEntityOwned`.
- struct PublicKeyCredentialUserEntityOwnedVisitor<'a, 'b, const L: usize>(
- PhantomData<fn() -> (&'a (), &'b ())>,
- );
- impl<'d: 'a + 'b, 'a, 'b, const L: usize> Visitor<'d>
- for PublicKeyCredentialUserEntityOwnedVisitor<'a, 'b, L>
+ struct PublicKeyCredentialUserEntityOwnedVisitor<const L: usize>;
+ impl<'d, const L: usize> Visitor<'d> for PublicKeyCredentialUserEntityOwnedVisitor<L>
where
UserHandle<L>: Deserialize<'d>,
{
- type Value = PublicKeyCredentialUserEntityOwned<'a, 'b, L>;
+ type Value = PublicKeyCredentialUserEntityOwned<L>;
fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
formatter.write_str("PublicKeyCredentialUserEntityOwned")
}
@@ -1539,7 +1328,7 @@ where
deserializer.deserialize_struct(
"PublicKeyCredentialUserEntityOwned",
FIELDS,
- PublicKeyCredentialUserEntityOwnedVisitor(PhantomData),
+ PublicKeyCredentialUserEntityOwnedVisitor,
)
}
}
@@ -2473,15 +2262,11 @@ impl<'de> Deserialize<'de> for ExtensionOwned {
///
/// This is primarily useful to assist [`ClientCredentialCreationOptions::deserialize`].
#[derive(Debug)]
-pub struct PublicKeyCredentialCreationOptionsOwned<
- 'user_name,
- 'user_display_name,
- const USER_LEN: usize,
-> {
+pub struct PublicKeyCredentialCreationOptionsOwned<const USER_LEN: usize> {
/// See [`PublicKeyCredentialCreationOptions::rp_id`].
pub rp_id: Option<RpId>,
/// See [`PublicKeyCredentialCreationOptions::user`].
- pub user: PublicKeyCredentialUserEntityOwned<'user_name, 'user_display_name, USER_LEN>,
+ pub user: PublicKeyCredentialUserEntityOwned<USER_LEN>,
/// See [`PublicKeyCredentialCreationOptions::pub_key_cred_params`].
pub pub_key_cred_params: CoseAlgorithmIdentifiers,
/// See [`PublicKeyCredentialCreationOptions::timeout`].
@@ -2519,7 +2304,7 @@ impl From<PublicKeyCredentialUserEntityOwnedErr> for PublicKeyCredentialCreation
Self::UserEntity(value)
}
}
-impl<const USER_LEN: usize> PublicKeyCredentialCreationOptionsOwned<'_, '_, USER_LEN> {
+impl<const USER_LEN: usize> PublicKeyCredentialCreationOptionsOwned<USER_LEN> {
/// Returns a `PublicKeyCredentialCreationOptions` based on `self` and `exclude_credentials`.
///
/// # Errors
@@ -2815,7 +2600,7 @@ impl<const USER_LEN: usize> PublicKeyCredentialCreationOptionsOwned<'_, '_, USER
}
}
}
-impl<const USER_LEN: usize> Default for PublicKeyCredentialCreationOptionsOwned<'_, '_, USER_LEN> {
+impl<const USER_LEN: usize> Default for PublicKeyCredentialCreationOptionsOwned<USER_LEN> {
#[inline]
fn default() -> Self {
Self {
@@ -2833,11 +2618,10 @@ impl<const USER_LEN: usize> Default for PublicKeyCredentialCreationOptionsOwned<
}
}
}
-impl<'de: 'user_name + 'user_display_name, 'user_name, 'user_display_name, const USER_LEN: usize>
- Deserialize<'de>
- for PublicKeyCredentialCreationOptionsOwned<'user_name, 'user_display_name, USER_LEN>
+impl<'de, const USER_LEN: usize> Deserialize<'de>
+ for PublicKeyCredentialCreationOptionsOwned<USER_LEN>
where
- PublicKeyCredentialUserEntityOwned<'user_name, 'user_display_name, USER_LEN>: Deserialize<'de>,
+ PublicKeyCredentialUserEntityOwned<USER_LEN>: Deserialize<'de>,
{
/// Deserializes a `struct` based on
/// [`PublicKeyCredentialCreationOptionsJSON`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialcreationoptionsjson).
@@ -2871,15 +2655,12 @@ where
D: Deserializer<'de>,
{
/// `Visitor` for `PublicKeyCredentialCreationOptionsOwned`.
- struct PublicKeyCredentialCreationOptionsOwnedVisitor<'a, 'b, const LEN: usize>(
- PhantomData<fn() -> (&'a (), &'b ())>,
- );
- impl<'d: 'a + 'b, 'a, 'b, const LEN: usize> Visitor<'d>
- for PublicKeyCredentialCreationOptionsOwnedVisitor<'a, 'b, LEN>
+ struct PublicKeyCredentialCreationOptionsOwnedVisitor<const LEN: usize>;
+ impl<'d, const LEN: usize> Visitor<'d> for PublicKeyCredentialCreationOptionsOwnedVisitor<LEN>
where
- PublicKeyCredentialUserEntityOwned<'a, 'b, LEN>: Deserialize<'d>,
+ PublicKeyCredentialUserEntityOwned<LEN>: Deserialize<'d>,
{
- type Value = PublicKeyCredentialCreationOptionsOwned<'a, 'b, LEN>;
+ type Value = PublicKeyCredentialCreationOptionsOwned<LEN>;
fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
formatter.write_str("PublicKeyCredentialCreationOptionsOwned")
}
@@ -3069,7 +2850,7 @@ where
deserializer.deserialize_struct(
"PublicKeyCredentialCreationOptionsOwned",
FIELDS,
- PublicKeyCredentialCreationOptionsOwnedVisitor(PhantomData),
+ PublicKeyCredentialCreationOptionsOwnedVisitor,
)
}
}
@@ -3085,18 +2866,15 @@ where
///
/// To facilitate this, [`Self::deserialize`] can be used to deserialize the data sent from the client.
#[derive(Debug)]
-pub struct ClientCredentialCreationOptions<'user_name, 'user_display_name, const USER_LEN: usize> {
+pub struct ClientCredentialCreationOptions<const USER_LEN: usize> {
/// See [`CredentialCreationOptions::mediation`].
pub mediation: CredentialMediationRequirement,
/// See [`CredentialCreationOptions::public_key`].
- pub public_key:
- PublicKeyCredentialCreationOptionsOwned<'user_name, 'user_display_name, USER_LEN>,
+ pub public_key: PublicKeyCredentialCreationOptionsOwned<USER_LEN>,
}
-impl<'de: 'user_name + 'user_display_name, 'user_name, 'user_display_name, const USER_LEN: usize>
- Deserialize<'de> for ClientCredentialCreationOptions<'user_name, 'user_display_name, USER_LEN>
+impl<'de, const USER_LEN: usize> Deserialize<'de> for ClientCredentialCreationOptions<USER_LEN>
where
- PublicKeyCredentialCreationOptionsOwned<'user_name, 'user_display_name, USER_LEN>:
- Deserialize<'de>,
+ PublicKeyCredentialCreationOptionsOwned<USER_LEN>: Deserialize<'de>,
{
/// Deserializes a `struct` according to the following pseudo-schema:
///
@@ -3118,15 +2896,12 @@ where
D: Deserializer<'de>,
{
/// `Visitor` for `ClientCredentialCreationOptions`.
- struct ClientCredentialCreationOptionsVisitor<'a, 'b, const LEN: usize>(
- PhantomData<fn() -> (&'a (), &'b ())>,
- );
- impl<'d: 'a + 'b, 'a, 'b, const LEN: usize> Visitor<'d>
- for ClientCredentialCreationOptionsVisitor<'a, 'b, LEN>
+ struct ClientCredentialCreationOptionsVisitor<const LEN: usize>;
+ impl<'d, const LEN: usize> Visitor<'d> for ClientCredentialCreationOptionsVisitor<LEN>
where
- PublicKeyCredentialCreationOptionsOwned<'a, 'b, LEN>: Deserialize<'d>,
+ PublicKeyCredentialCreationOptionsOwned<LEN>: Deserialize<'d>,
{
- type Value = ClientCredentialCreationOptions<'a, 'b, LEN>;
+ type Value = ClientCredentialCreationOptions<LEN>;
fn expecting(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
formatter.write_str("ClientCredentialCreationOptions")
}
@@ -3196,7 +2971,7 @@ where
deserializer.deserialize_struct(
"ClientCredentialCreationOptions",
FIELDS,
- ClientCredentialCreationOptionsVisitor(PhantomData),
+ ClientCredentialCreationOptionsVisitor,
)
}
}
@@ -3223,13 +2998,13 @@ mod test {
#[test]
fn client_options() -> Result<(), Error> {
let mut err =
- serde_json::from_str::<ClientCredentialCreationOptions<'_, '_, 16>>(r#"{"bob":true}"#)
+ serde_json::from_str::<ClientCredentialCreationOptions<16>>(r#"{"bob":true}"#)
.unwrap_err();
assert_eq!(
err.to_string().get(..56),
Some("unknown field `bob`, expected `mediation` or `publicKey`")
);
- err = serde_json::from_str::<ClientCredentialCreationOptions<'_, '_, 1>>(
+ err = serde_json::from_str::<ClientCredentialCreationOptions<1>>(
r#"{"mediation":"required","mediation":"required"}"#,
)
.unwrap_err();
@@ -3237,7 +3012,7 @@ mod test {
err.to_string().get(..27),
Some("duplicate field `mediation`")
);
- let mut options = serde_json::from_str::<ClientCredentialCreationOptions<'_, '_, 1>>("{}")?;
+ let mut options = serde_json::from_str::<ClientCredentialCreationOptions<1>>("{}")?;
assert!(matches!(
options.mediation,
CredentialMediationRequirement::Required
@@ -3273,7 +3048,7 @@ mod test {
));
assert!(options.public_key.extensions.min_pin_length.is_none());
assert!(options.public_key.extensions.prf.is_none());
- options = serde_json::from_str::<ClientCredentialCreationOptions<'_, '_, 1>>(
+ options = serde_json::from_str::<ClientCredentialCreationOptions<1>>(
r#"{"mediation":null,"publicKey":null}"#,
)?;
assert!(matches!(
@@ -3311,9 +3086,8 @@ mod test {
));
assert!(options.public_key.extensions.min_pin_length.is_none());
assert!(options.public_key.extensions.prf.is_none());
- options = serde_json::from_str::<ClientCredentialCreationOptions<'_, '_, 1>>(
- r#"{"publicKey":{}}"#,
- )?;
+ options =
+ serde_json::from_str::<ClientCredentialCreationOptions<1>>(r#"{"publicKey":{}}"#)?;
assert!(options.public_key.rp_id.is_none());
assert!(options.public_key.user.name.is_none());
assert!(options.public_key.user.id.is_none());
@@ -3345,7 +3119,7 @@ mod test {
));
assert!(options.public_key.extensions.min_pin_length.is_none());
assert!(options.public_key.extensions.prf.is_none());
- options = serde_json::from_str::<ClientCredentialCreationOptions<'_, '_, 1>>(
+ options = serde_json::from_str::<ClientCredentialCreationOptions<1>>(
r#"{"mediation":"conditional","publicKey":{"rp":{"name":"Example.com","id":"example.com"},"user":{"name":"bob","displayName":"Bob","id":"AQ"},"timeout":300000,"excludeCredentials":[],"attestation":"none","attestationFormats":["none"],"authenticatorSelection":{"authenticatorAttachment":"cross-platform","residentKey":"required","requireResidentKey":true,"userVerification":"required"},"extensions":{"credProps":true,"credentialProtectionPolicy":"userVerificationRequired","enforceCredentialProtectionPolicy":false,"minPinLength":true,"prf":{"eval":{"first":"","second":""}}},"pubKeyCredParams":[{"type":"public-key","alg":-8}],"hints":["security-key"],"challenge":null}}"#,
)?;
assert!(matches!(
@@ -3358,19 +3132,13 @@ mod test {
.rp_id
.is_some_and(|val| val.as_ref() == "example.com")
);
- assert!(
- options
- .public_key
- .user
- .name
- .is_some_and(|val| val.as_ref() == "bob")
- );
+ assert!(options.public_key.user.name.is_some_and(|val| val == "bob"));
assert!(
options
.public_key
.user
.display_name
- .is_some_and(|val| val.as_ref() == "Bob")
+ .is_some_and(|val| val == "Bob")
);
assert!(
options
@@ -3447,17 +3215,16 @@ mod test {
)]
#[test]
fn key_options() -> Result<(), Error> {
- let mut err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 16>>(
- r#"{"bob":true}"#,
- )
- .unwrap_err();
+ let mut err =
+ serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<16>>(r#"{"bob":true}"#)
+ .unwrap_err();
assert_eq!(
err.to_string().get(..201),
Some(
"unknown field `bob`, expected one of `rp`, `user`, `challenge`, `pubKeyCredParams`, `timeout`, `excludeCredentials`, `authenticatorSelection`, `hints`, `extensions`, `attestation`, `attestationFormats`"
)
);
- err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 1>>(
+ err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<1>>(
r#"{"attestation":"none","attestation":"none"}"#,
)
.unwrap_err();
@@ -3465,7 +3232,7 @@ mod test {
err.to_string().get(..29),
Some("duplicate field `attestation`")
);
- err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 1>>(
+ err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<1>>(
r#"{"challenge":"AAAAAAAAAAAAAAAAAAAAAA"}"#,
)
.unwrap_err();
@@ -3473,12 +3240,12 @@ mod test {
err.to_string().get(..41),
Some("invalid type: Option value, expected null")
);
- err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 1>>(
+ err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<1>>(
r#"{"excludeCredentials":[{"type":"public-key","transports":["usb"],"id":"AAAAAAAAAAAAAAAAAAAAAA"}]}"#,
)
.unwrap_err();
assert_eq!(err.to_string().get(..19), Some("trailing characters"));
- err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 1>>(
+ err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<1>>(
r#"{"attestation":"foo"}"#,
)
.unwrap_err();
@@ -3486,7 +3253,7 @@ mod test {
err.to_string().get(..27),
Some("invalid value: string \"foo\"")
);
- err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 1>>(
+ err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<1>>(
r#"{"attestationFormats":["none","none"]}"#,
)
.unwrap_err();
@@ -3496,7 +3263,7 @@ mod test {
"attestationFormats must be an empty sequence or contain exactly one string whose value is 'none'"
)
);
- err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 1>>(
+ err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<1>>(
r#"{"attestationFormats":["foo"]}"#,
)
.unwrap_err();
@@ -3504,15 +3271,14 @@ mod test {
err.to_string().get(..42),
Some("invalid value: string \"foo\", expected none")
);
- err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 1>>(
- r#"{"timeout":0}"#,
- )
- .unwrap_err();
+ err =
+ serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<1>>(r#"{"timeout":0}"#)
+ .unwrap_err();
assert_eq!(
err.to_string().get(..50),
Some("invalid value: integer `0`, expected a nonzero u32")
);
- err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 1>>(
+ err = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<1>>(
r#"{"timeout":4294967296}"#,
)
.unwrap_err();
@@ -3520,8 +3286,7 @@ mod test {
err.to_string().get(..59),
Some("invalid value: integer `4294967296`, expected a nonzero u32")
);
- let mut key =
- serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 1>>("{}")?;
+ let mut key = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<1>>("{}")?;
assert!(key.rp_id.is_none());
assert!(key.user.name.is_none());
assert!(key.user.id.is_none());
@@ -3544,7 +3309,7 @@ mod test {
assert!(matches!(key.extensions.cred_protect, CredProtect::None));
assert!(key.extensions.min_pin_length.is_none());
assert!(key.extensions.prf.is_none());
- key = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 1>>(
+ key = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<1>>(
r#"{"rp":null,"user":null,"timeout":null,"excludeCredentials":null,"attestation":null,"attestationFormats":null,"authenticatorSelection":null,"extensions":null,"pubKeyCredParams":null,"hints":null,"challenge":null}"#,
)?;
assert!(key.rp_id.is_none());
@@ -3569,7 +3334,7 @@ mod test {
assert!(matches!(key.extensions.cred_protect, CredProtect::None));
assert!(key.extensions.min_pin_length.is_none());
assert!(key.extensions.prf.is_none());
- key = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 1>>(
+ key = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<1>>(
r#"{"rp":{},"user":{},"excludeCredentials":[],"attestationFormats":[],"authenticatorSelection":{},"extensions":{},"pubKeyCredParams":[],"hints":[]}"#,
)?;
assert!(key.rp_id.is_none());
@@ -3593,7 +3358,7 @@ mod test {
assert!(matches!(key.extensions.cred_protect, CredProtect::None));
assert!(key.extensions.min_pin_length.is_none());
assert!(key.extensions.prf.is_none());
- key = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 1>>(
+ key = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<1>>(
r#"{"rp":{"name":null,"id":null},"user":{"name":null,"id":null,"displayName":null},"authenticatorSelection":{"residentKey":null,"requireResidentKey":null,"userVerification":null,"authenticatorAttachment":null},"extensions":{"credProps":null,"credentialProtectionPolicy":null,"enforceCredentialProtectionPolicy":null,"minPinLength":null,"prf":null}}"#,
)?;
assert!(key.rp_id.is_none());
@@ -3617,16 +3382,12 @@ mod test {
assert!(matches!(key.extensions.cred_protect, CredProtect::None));
assert!(key.extensions.min_pin_length.is_none());
assert!(key.extensions.prf.is_none());
- key = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 1>>(
+ key = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<1>>(
r#"{"rp":{"name":"Example.com","id":"example.com"},"user":{"name":"bob","displayName":"Bob","id":"AQ"},"timeout":300000,"excludeCredentials":[],"attestation":"none","attestationFormats":["none"],"authenticatorSelection":{"authenticatorAttachment":"cross-platform","residentKey":"required","requireResidentKey":true,"userVerification":"required"},"extensions":{"credProps":true,"credentialProtectionPolicy":"userVerificationRequired","enforceCredentialProtectionPolicy":false,"minPinLength":true,"prf":{"eval":{"first":"","second":""}}},"pubKeyCredParams":[{"type":"public-key","alg":-8}],"hints":["security-key"],"challenge":null}"#,
)?;
assert!(key.rp_id.is_some_and(|val| val.as_ref() == "example.com"));
- assert!(key.user.name.is_some_and(|val| val.as_ref() == "bob"));
- assert!(
- key.user
- .display_name
- .is_some_and(|val| val.as_ref() == "Bob")
- );
+ assert!(key.user.name.is_some_and(|val| val == "bob"));
+ assert!(key.user.display_name.is_some_and(|val| val == "Bob"));
assert!(key.user.id.is_some_and(|val| val.as_ref() == [1; 1]));
assert_eq!(
key.pub_key_cred_params.0,
@@ -3669,7 +3430,7 @@ mod test {
assert!(key.extensions.prf.is_some_and(|prf| prf.first.is_empty()
&& prf.second.is_some_and(|p| p.is_empty())
&& matches!(prf.ext_req, ExtensionReq::Allow)));
- key = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<'_, '_, 1>>(
+ key = serde_json::from_str::<PublicKeyCredentialCreationOptionsOwned<1>>(
r#"{"timeout":4294967295}"#,
)?;
assert_eq!(key.timeout, NonZeroU32::MAX);
@@ -3768,35 +3529,34 @@ mod test {
)]
#[test]
fn user_entity() -> Result<(), Error> {
- let mut err = serde_json::from_str::<PublicKeyCredentialUserEntityOwned<'_, '_, 16>>(
- r#"{"bob":true}"#,
- )
- .unwrap_err();
+ let mut err =
+ serde_json::from_str::<PublicKeyCredentialUserEntityOwned<16>>(r#"{"bob":true}"#)
+ .unwrap_err();
assert_eq!(
err.to_string().get(..64),
Some("unknown field `bob`, expected one of `id`, `name`, `displayName`")
);
- err = serde_json::from_str::<PublicKeyCredentialUserEntityOwned<'_, '_, 1>>(
+ err = serde_json::from_str::<PublicKeyCredentialUserEntityOwned<1>>(
r#"{"name":"bob","name":"bob"}"#,
)
.unwrap_err();
assert_eq!(err.to_string().get(..22), Some("duplicate field `name`"));
- let mut user = serde_json::from_str::<PublicKeyCredentialUserEntityOwned<'_, '_, 1>>(
+ let mut user = serde_json::from_str::<PublicKeyCredentialUserEntityOwned<1>>(
r#"{"id":"AQ","name":"bob","displayName":"Bob"}"#,
)?;
assert!(
user.id
.is_some_and(|val| val.as_slice() == [1; 1].as_slice())
);
- assert!(user.name.is_some_and(|val| val.as_ref() == "bob"));
- assert!(user.display_name.is_some_and(|val| val.as_ref() == "Bob"));
- user = serde_json::from_str::<PublicKeyCredentialUserEntityOwned<'_, '_, 1>>(
+ assert!(user.name.is_some_and(|val| val == "bob"));
+ assert!(user.display_name.is_some_and(|val| val == "Bob"));
+ user = serde_json::from_str::<PublicKeyCredentialUserEntityOwned<1>>(
r#"{"id":null,"name":null,"displayName":null}"#,
)?;
assert!(user.name.is_none());
assert!(user.display_name.is_none());
assert!(user.id.is_none());
- user = serde_json::from_str::<PublicKeyCredentialUserEntityOwned<'_, '_, 1>>("{}")?;
+ user = serde_json::from_str::<PublicKeyCredentialUserEntityOwned<1>>("{}")?;
assert!(user.name.is_none());
assert!(user.display_name.is_none());
assert!(user.id.is_none());
diff --git a/src/response.rs b/src/response.rs
@@ -143,7 +143,7 @@ pub mod error;
/// # use core::convert;
/// # use webauthn_rp::{
/// # hash::hash_set::{InsertRemoveExpired, MaxLenHashSet},
-/// # request::{register::{error::CreationOptionsErr, CredentialCreationOptions, DisplayName, PublicKeyCredentialUserEntity, RegistrationClientState, UserHandle, UserHandle64, USER_HANDLE_MAX_LEN, RegistrationVerificationOptions}, PublicKeyCredentialDescriptor, RpId},
+/// # request::{register::{error::CreationOptionsErr, CredentialCreationOptions, PublicKeyCredentialUserEntity, RegistrationClientState, UserHandle, UserHandle64, USER_HANDLE_MAX_LEN, RegistrationVerificationOptions}, PublicKeyCredentialDescriptor, RpId},
/// # response::{register::{error::RegCeremonyErr, Registration}, error::CollectedClientDataErr, CollectedClientData},
/// # RegisteredCredential
/// # };
@@ -209,9 +209,9 @@ pub mod error;
/// fn get_user_entity(user: &UserHandle<USER_HANDLE_MAX_LEN>) -> PublicKeyCredentialUserEntity<'_, '_, '_, USER_HANDLE_MAX_LEN> {
/// // ⋮
/// # PublicKeyCredentialUserEntity {
-/// # name: "foo".try_into().unwrap(),
+/// # name: "foo",
/// # id: user,
-/// # display_name: DisplayName::Blank,
+/// # display_name: "",
/// # }
/// }
/// /// Send `RegistrationClientState` and receive `Registration` JSON from client.
diff --git a/src/response/ser.rs b/src/response/ser.rs
@@ -861,15 +861,15 @@ where
/// # #[cfg(feature = "bin")]
/// # use webauthn_rp::bin::Decode;
/// # use webauthn_rp::{
- /// # request::{register::{DisplayName, PublicKeyCredentialUserEntity, UserHandle, USER_HANDLE_MIN_LEN, Username}, AsciiDomain, RpId},
+ /// # request::{register::{PublicKeyCredentialUserEntity, UserHandle, USER_HANDLE_MIN_LEN}, AsciiDomain, RpId},
/// # response::CurrentUserDetailsOptions,
/// # AggErr,
/// # };
/// /// Retrieves the `PublicKeyCredentialUserEntity` info associated with `user_id` from the database.
/// # #[cfg(feature = "bin")]
- /// fn get_user_info(user_id: UserHandle<USER_HANDLE_MIN_LEN>) -> Result<(Username<'static>, DisplayName<'static>), AggErr> {
+ /// fn get_user_info(user_id: UserHandle<USER_HANDLE_MIN_LEN>) -> Result<(String, String), AggErr> {
/// // ⋮
- /// # Ok((Username::decode("foo").unwrap(), DisplayName::decode("foo").unwrap()))
+ /// # Ok(("foo".to_owned(), "foo".to_owned()))
/// }
/// /// Retrieves the `UserHandle` from a session cookie.
/// # #[cfg(feature = "custom")]
@@ -885,7 +885,7 @@ where
/// assert_eq!(
/// serde_json::to_string(&CurrentUserDetailsOptions {
/// rp_id: &RpId::Domain(AsciiDomain::try_from("example.com".to_owned())?),
- /// user: PublicKeyCredentialUserEntity { name, id: &user_handle, display_name, },
+ /// user: PublicKeyCredentialUserEntity { name: &name, id: &user_handle, display_name: &display_name, },
/// })
/// .unwrap(),
/// r#"{"rpId":"example.com","userId":"AA","name":"foo","displayName":"foo"}"#