# = Action Pack WebAuthn Attestation Response # # Handles the authenticator response from a WebAuthn registration ceremony. # When a user registers a new credential, the authenticator returns an # attestation response containing the new public key and credential ID. # # == Usage # # response = ActionPack::WebAuthn::Authenticator::AttestationResponse.new( # client_data_json: params[:response][:clientDataJSON], # attestation_object: params[:response][:attestationObject], # origin: "https://example.com" # ) # # response.validate! # # # Store the credential # credential_id = response.attestation.credential_id # public_key = response.attestation.public_key # # == Validation # # In addition to the base Response validations, this class verifies: # # * The client data type is "webauthn.create" # * The attestation format has a registered verifier # * The attestation statement passes format-specific verification # class ActionPack::WebAuthn::Authenticator::AttestationResponse < ActionPack::WebAuthn::Authenticator::Response attr_reader :attestation_object validate :client_data_type_must_be_create validate :attestation_must_be_valid def initialize(attestation_object:, **attributes) super(**attributes) @attestation_object = attestation_object end # Returns the decoded Attestation object, lazily parsed from the raw # attestation object bytes. def attestation @attestation ||= ActionPack::WebAuthn::Authenticator::Attestation.wrap(attestation_object) end # Returns the authenticator data extracted from the attestation object. def authenticator_data attestation.authenticator_data end private def client_data_type_must_be_create unless client_data["type"] == "webauthn.create" errors.add(:base, "Client data type is not webauthn.create") end end def attestation_must_be_valid verifier = ActionPack::WebAuthn.attestation_verifiers[attestation.format] if verifier verifier.verify!(attestation, client_data_json: client_data_json) else errors.add(:base, "Unsupported attestation format: #{attestation.format}") end end end