diff --git a/lib/webauthn/authenticator_assertion_response.rb b/lib/webauthn/authenticator_assertion_response.rb index 6ae46482..8dc3e3c0 100644 --- a/lib/webauthn/authenticator_assertion_response.rb +++ b/lib/webauthn/authenticator_assertion_response.rb @@ -11,6 +11,7 @@ class SignCountVerificationError < VerificationError; end class AuthenticatorAssertionResponse < AuthenticatorResponse def self.from_client(response, relying_party: WebAuthn.configuration.relying_party) + validate_client_response!(response, %w[authenticatorData clientDataJSON signature]) encoder = relying_party.encoder user_handle = diff --git a/lib/webauthn/authenticator_attestation_response.rb b/lib/webauthn/authenticator_attestation_response.rb index 101832a5..38ea1f0e 100644 --- a/lib/webauthn/authenticator_attestation_response.rb +++ b/lib/webauthn/authenticator_attestation_response.rb @@ -18,6 +18,7 @@ class AuthenticatorAttestationResponse < AuthenticatorResponse extend Forwardable def self.from_client(response, relying_party: WebAuthn.configuration.relying_party) + validate_client_response!(response, %w[attestationObject clientDataJSON]) encoder = relying_party.encoder new( diff --git a/lib/webauthn/authenticator_response.rb b/lib/webauthn/authenticator_response.rb index c31f45d4..bdb98fc9 100644 --- a/lib/webauthn/authenticator_response.rb +++ b/lib/webauthn/authenticator_response.rb @@ -20,6 +20,12 @@ class UserPresenceVerificationError < VerificationError; end class UserVerifiedVerificationError < VerificationError; end class AuthenticatorResponse + def self.validate_client_response!(response, required_fields) + unless response.is_a?(Hash) && required_fields.all? { |field| response[field].respond_to?(:to_str) } + raise CredentialFormatError, "Credential response must contain string-like #{required_fields.join(', ')} fields" + end + end + def initialize(client_data_json:, relying_party: WebAuthn.configuration.relying_party) @client_data_json = client_data_json @relying_party = relying_party diff --git a/lib/webauthn/error.rb b/lib/webauthn/error.rb index 8b522f35..172be93b 100644 --- a/lib/webauthn/error.rb +++ b/lib/webauthn/error.rb @@ -2,4 +2,5 @@ module WebAuthn class Error < StandardError; end + class CredentialFormatError < Error; end end diff --git a/lib/webauthn/public_key_credential.rb b/lib/webauthn/public_key_credential.rb index 5be6c983..00c919d3 100644 --- a/lib/webauthn/public_key_credential.rb +++ b/lib/webauthn/public_key_credential.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "webauthn/encoder" +require "webauthn/error" module WebAuthn class PublicKeyCredential @@ -9,6 +10,11 @@ class InvalidChallengeError < Error; end attr_reader :type, :id, :raw_id, :client_extension_outputs, :authenticator_attachment, :response def self.from_client(credential, relying_party: WebAuthn.configuration.relying_party) + unless credential.is_a?(Hash) && %w[type id rawId].all? { |field| credential[field].respond_to?(:to_str) } && + credential["response"].is_a?(Hash) + raise CredentialFormatError, "Credential must contain string-like type, id, rawId and an object response" + end + new( type: credential["type"], id: credential["id"],