Module: Unmagic::Passkeys::Test::Helpers

Defined in:
lib/unmagic/passkeys/test/helpers.rb

Overview

Mints valid WebAuthn ceremony payloads from a fixed EC P-256 key pair, so passkey registration and authentication can be exercised end to end without a browser. Ported from fizzy's test helper.

In a Rails app, requiring this file auto-includes it into integration tests. Elsewhere (e.g. RSpec) include it yourself:

require "unmagic/passkeys/test/helpers"
RSpec.configure { |c| c.include Unmagic::Passkeys::Test::Helpers }

The relying party defaults to "www.example.com" / "http://www.example.com" (the Rails integration-test host, so the engine's request context lines up automatically). Override webauthn_rp_id / webauthn_origin in your test class to test under a different host.

Constant Summary collapse

WEBAUTHN_PRIVATE_KEY =
OpenSSL::PKey::EC.new(
  [ "307702010104201dd589de7210b3318620f32150e3012cce021519df1d6e9e01" \
    "0471146d395cdca00a06082a8648ce3d030107a14403420004116847fe19e1ad" \
    "4471ab9980d7ff9cc1e4c7cb7a3af00e082b64fcd84f5ae70114c2495eef16f" \
    "542b5e57dd1b263661624e3cf28f581b57a441edbd756a41d0e" ].pack("H*")
)
COSE_PUBLIC_KEY =

Pre-encoded COSE EC2/ES256 public key (CBOR) for the key above.

[ "a5010203262001215820116847fe19e1ad4471ab9980d7ff9cc1" \
"e4c7cb7a3af00e082b64fcd84f5ae70122582014c2495eef16f542b5e57dd1b2" \
"63661624e3cf28f581b57a441edbd756a41d0e" ].pack("H*")
ATTESTATION_OBJECT_CBOR_PREFIX =

CBOR prefix for "none", "attStmt": {, "authData": bytes(164)}.

[ "a363666d74646e6f6e656761747453746d74a068617574684461746158a4" ].pack("H*")
RP_ID =
"www.example.com"
ORIGIN =
"http://www.example.com"

Instance Method Summary collapse

Instance Method Details

#build_assertion_params(challenge:, credential:, sign_count: 1) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/unmagic/passkeys/test/helpers.rb', line 83

def build_assertion_params(challenge:, credential:, sign_count: 1)
  client_data_json = webauthn_client_data_json(challenge: challenge, type: "webauthn.get")
  authenticator_data = build_assertion_auth_data(sign_count: sign_count)
  signature = webauthn_sign(authenticator_data, client_data_json)

  {
    id: credential.credential_id,
    client_data_json: client_data_json,
    authenticator_data: Base64.urlsafe_encode64(authenticator_data, padding: false),
    signature: Base64.urlsafe_encode64(signature, padding: false)
  }
end

#build_attestation_params(challenge:) ⇒ Object



72
73
74
75
76
77
78
79
80
81
# File 'lib/unmagic/passkeys/test/helpers.rb', line 72

def build_attestation_params(challenge:)
  credential_id = SecureRandom.random_bytes(32)
  auth_data = build_attestation_auth_data(credential_id: credential_id)

  {
    client_data_json: webauthn_client_data_json(challenge: challenge, type: "webauthn.create"),
    attestation_object: Base64.urlsafe_encode64(ATTESTATION_OBJECT_CBOR_PREFIX + auth_data, padding: false),
    transports: [ "internal" ]
  }
end

#register_passkey_for(holder) ⇒ Object

Registers a passkey for holder directly (used to set up the "already enrolled" state). Returns the persisted credential.



53
54
55
56
57
# File 'lib/unmagic/passkeys/test/helpers.rb', line 53

def register_passkey_for(holder)
  with_webauthn_request_context do
    holder.passkeys.register(build_attestation_params(challenge: webauthn_challenge(purpose: "registration")))
  end
end

#webauthn_challenge(purpose: nil) ⇒ Object



68
69
70
# File 'lib/unmagic/passkeys/test/helpers.rb', line 68

def webauthn_challenge(purpose: nil)
  Unmagic::Passkeys::WebAuthn::PublicKeyCredential::Options.new(challenge_purpose: purpose).challenge
end

#webauthn_originObject



49
# File 'lib/unmagic/passkeys/test/helpers.rb', line 49

def webauthn_origin = ORIGIN

#webauthn_rp_idObject

The relying party identity used to mint payloads. Override in a test class to exercise a different host.



48
# File 'lib/unmagic/passkeys/test/helpers.rb', line 48

def webauthn_rp_id = RP_ID

#with_webauthn_request_contextObject Also known as: in_webauthn_context



59
60
61
62
63
64
65
# File 'lib/unmagic/passkeys/test/helpers.rb', line 59

def with_webauthn_request_context
  Unmagic::Passkeys::WebAuthn::Current.host = webauthn_rp_id
  Unmagic::Passkeys::WebAuthn::Current.origin = webauthn_origin
  yield
ensure
  Unmagic::Passkeys::WebAuthn::Current.reset
end