Created
July 8, 2024 13:48
-
-
Save jacegu/9d1de1c1d7f804917f70f3ee1df30971 to your computer and use it in GitHub Desktop.
Stubbing WebAuthn credential verification in automated tests.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
require 'webauthn' | |
require 'webauthn/fake_client' | |
# @see https://github.com/cedarcode/webauthn-ruby?tab=readme-ov-file#testing-your-integration | |
def stub_webauthn_verification | |
# Generate FakeClient with configured origin. | |
client = WebAuthn::FakeClient.new(DnsimpleConfig.webauthn_origin) | |
# Stub challenge. Will be used both for #create and #get. | |
challenge = Base64.urlsafe_encode64(SecureRandom.bytes(8)) | |
# Create credential with stubbed values | |
create_result = client.create(challenge:) | |
# Generate response to be able to extract credential deta relevant for verification. | |
relying_party = WebAuthn.configuration.relying_party | |
attestation_object = relying_party.encoder.decode(create_result["response"]["attestationObject"]) | |
client_data_json = relying_party.encoder.decode(create_result["response"]["clientDataJSON"]) | |
response = | |
WebAuthn::AuthenticatorAttestationResponse | |
.new( | |
attestation_object:, | |
client_data_json:, | |
relying_party: | |
) | |
# Generate payload for WebAuthn::Credential.from_get call. | |
# There is a temporal coupling here, this needs to happen _after_ the call to #create. | |
get_result = client.get(challenge:) | |
{ | |
payload: get_result, | |
challenge:, | |
external_id: create_result["id"], | |
public_key: Base64.urlsafe_encode64(response.credential.public_key), | |
sign_count: response.authenticator_data.sign_count, | |
} | |
end | |
# You usually retrieve your authentication mean based on the external_id. | |
# For convenience we will store public key, and sign count there. | |
FactoryBot.create(:security_key, public_key:, sign_count:) | |
# Verification goes like: | |
credential = WebAuthn::Credential.from_get(payload) | |
credential.verify(challenge, public_key:, sign_count:) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment