Skip to content

Conversation

@AlfioEmanueleFresta
Copy link
Member

@AlfioEmanueleFresta AlfioEmanueleFresta commented Jul 27, 2025

No description provided.

@AlfioEmanueleFresta AlfioEmanueleFresta changed the title [WIP] Web IDL support (make credentials); [WIP] Web IDL support Jul 27, 2025
@AlfioEmanueleFresta
Copy link
Member Author

AlfioEmanueleFresta commented Aug 15, 2025

This now covers both MC and GA requests, but not responses. I'll probably tidy this up and keep responses for a separate PR.

Note to self, the failures are due to Base64UrlString being used in CTAP models, which are serialized as a Base64 string rather than URL with serde_cbor. The next step is to separate these models into two, a WebAuthn JSON model using Base64UrlString, and an equivalent CTAP model using ByteBuf. Must also review all usages of Base64UrlString.

@AlfioEmanueleFresta AlfioEmanueleFresta changed the title [WIP] Web IDL support Web IDL support 1/N: Get assertion and make credentials basic parsing Sep 7, 2025
@AlfioEmanueleFresta AlfioEmanueleFresta changed the title Web IDL support 1/N: Get assertion and make credentials basic parsing Web IDL support 1/N: GA, MC basic parsing Sep 7, 2025
@AlfioEmanueleFresta
Copy link
Member Author

Marking this as ready for review as this is getting large, keeping track of further work in #134.

@AlfioEmanueleFresta AlfioEmanueleFresta marked this pull request as ready for review September 7, 2025 21:49
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces the foundation for Web IDL support by implementing basic parsing for GetAssertion (GA) and MakeCredential (MC) operations. The changes enable the library to parse WebAuthn operations from JSON format, which is a key requirement for Web IDL compliance.

  • Introduces a new IDL module with JSON parsing capabilities for WebAuthn operations
  • Refactors extension handling to use proper typed structures instead of enums
  • Adds support for parsing WebAuthn requests from JSON format with proper validation

Reviewed Changes

Copilot reviewed 24 out of 25 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
libwebauthn/src/ops/webauthn/mod.rs Adds IDL module exports and updates core WebAuthn type definitions
libwebauthn/src/ops/webauthn/idl/* New IDL parsing infrastructure with JSON deserialization support
libwebauthn/src/ops/webauthn/make_credential.rs Refactors extension handling and adds JSON parsing support
libwebauthn/src/ops/webauthn/get_assertion.rs Updates extension structures and adds JSON parsing capabilities
libwebauthn/src/proto/ctap2/model/* Updates extension handling to work with new typed structures
libwebauthn/src/tests/basic_ctap2.rs Updates test to use new extension format
Examples and other files Updates to use new extension APIs
Comments suppressed due to low confidence (1)

libwebauthn/src/proto/ctap2/model/make_credential.rs:1

  • The Default trait was removed from Ctap2GetAssertionRequestExtensions but line 142 in the same file still calls skip_serializing_extensions which expects a default value check. This could cause compilation issues.
use super::{

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +15 to +17
impl Into<String> for RelyingPartyId {
fn into(self) -> String {
self.0
Copy link

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider implementing From<RelyingPartyId> for String instead of Into<String> for RelyingPartyId. The From trait automatically provides the Into implementation and is the preferred approach in Rust.

Suggested change
impl Into<String> for RelyingPartyId {
fn into(self) -> String {
self.0
impl From<RelyingPartyId> for String {
fn from(rpid: RelyingPartyId) -> String {
rpid.0

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +43
impl Into<Ctap2PublicKeyCredentialDescriptor> for PublicKeyCredentialDescriptorJSON {
fn into(self) -> Ctap2PublicKeyCredentialDescriptor {
Ctap2PublicKeyCredentialDescriptor {
r#type: self.r#type,
id: ByteBuf::from(self.id),
transports: self.transports,
Copy link

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider implementing From<PublicKeyCredentialDescriptorJSON> for Ctap2PublicKeyCredentialDescriptor instead of Into. The From trait automatically provides the Into implementation and is the preferred approach in Rust.

Suggested change
impl Into<Ctap2PublicKeyCredentialDescriptor> for PublicKeyCredentialDescriptorJSON {
fn into(self) -> Ctap2PublicKeyCredentialDescriptor {
Ctap2PublicKeyCredentialDescriptor {
r#type: self.r#type,
id: ByteBuf::from(self.id),
transports: self.transports,
impl From<PublicKeyCredentialDescriptorJSON> for Ctap2PublicKeyCredentialDescriptor {
fn from(value: PublicKeyCredentialDescriptorJSON) -> Self {
Ctap2PublicKeyCredentialDescriptor {
r#type: value.r#type,
id: ByteBuf::from(value.id),
transports: value.transports,

Copilot uses AI. Check for mistakes.
Comment on lines +57 to +59
impl Into<Vec<u8>> for Base64UrlString {
fn into(self) -> Vec<u8> {
self.0
Copy link

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider implementing From<Base64UrlString> for Vec<u8> instead of Into<Vec<u8>>. The From trait automatically provides the Into implementation and is the preferred approach in Rust.

Suggested change
impl Into<Vec<u8>> for Base64UrlString {
fn into(self) -> Vec<u8> {
self.0
impl From<Base64UrlString> for Vec<u8> {
fn from(b64: Base64UrlString) -> Vec<u8> {
b64.0

Copilot uses AI. Check for mistakes.
Comment on lines +28 to +29
let json =
format!("{{\"type\":\"{op_str}\",\"challenge\":\"{challenge_str}\",\"origin\":\"{origin_str}\",\"crossOrigin\":{cross_origin_str}}}");
Copy link

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JSON string construction using format! is error-prone and hard to maintain. Consider using serde_json to serialize a proper struct to ensure valid JSON format.

Copilot uses AI. Check for mistakes.
Some(inner.exclude_credentials)
},
extensions: inner.extensions,
timeout: timeout,
Copy link

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant field initialization. When the field name matches the variable name, you can use the shorthand syntax: timeout instead of timeout: timeout.

Suggested change
timeout: timeout,
timeout,

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

@msirringhaus msirringhaus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good overall, I think. I haven't had time for a full deep dive, though. I only have some preliminary questions.


let eval_by_credential = HashMap::new();
let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf {
let hmac_or_prf: GetAssertionHmacOrPrfInput = GetAssertionHmacOrPrfInput::Prf(PrfInput {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the type specifier let hmac_or_prf: GetAssertionHmacOrPrfInput needed? I think it could be removed.

},
);
let hmac_or_prf = GetAssertionHmacOrPrfInput::Prf {
let hmac_or_prf: GetAssertionHmacOrPrfInput = GetAssertionHmacOrPrfInput::Prf(PrfInput {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here and below

(signed_extensions.hmac_secret, None)
}
MakeCredentialHmacOrPrfInput::Prf => (
if let Some(_hmac_create_secret) = incoming_ext.hmac_create_secret {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using if {} else if {} here means you can either have HMAC or PRF, and if you request both, HMAC will override PRF. Is that intended?


use super::{DowngradableRequest, RegisterRequest, UserVerificationRequirement};

pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use the defintion from libwebauthn/src/ops/webauthn/timeout.rs here? (Same in the other files where this is defined again)

pub hash: Vec<u8>,
pub allow: Vec<Ctap2PublicKeyCredentialDescriptor>,
pub extensions: Option<GetAssertionRequestExtensions>,
pub extensions: GetAssertionRequestExtensions,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I confess, I'm currently a bit lost in the details of this huge PR, but if I recall correctly, we used to use the Option<> here to decide if we have to include an extensions-map in the output. Some portals like demo.yubico.com are rather strict on what they expect, e.g. if they request extensions, they expect extensions in the response, even if the map is empty. And they do not want to see that empty map, if they didn't request one.
Is this still possible with this change?

use super::{DowngradableRequest, RelyingPartyId, SignRequest, UserVerificationRequirement};

#[derive(Debug, Default, Clone, Serialize)]
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question here as elsewhere: Should this be redefined here or imported?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants