Implement Passkey registration

- Create Identity credentials
- Add management UI and registration flow
This commit is contained in:
Stanko K.R.
2026-02-13 16:58:06 +01:00
parent 4bb0a2929c
commit 6c58fd9bdd
16 changed files with 330 additions and 19 deletions
@@ -63,10 +63,9 @@ class ActionPack::WebAuthn::PublicKeyCredential::CreationOptionsTest < ActiveSup
end
test "as_json includes excludeCredentials" do
credential = Struct.new(:id, :transports)
credentials = [
credential.new("cred-1", [ "usb", "nfc" ]),
credential.new("cred-2", [ "internal" ])
build_credential(id: "cred-1", transports: [ "usb", "nfc" ]),
build_credential(id: "cred-2", transports: [ "internal" ])
]
options = ActionPack::WebAuthn::PublicKeyCredential::CreationOptions.new(
@@ -84,13 +83,11 @@ class ActionPack::WebAuthn::PublicKeyCredential::CreationOptionsTest < ActiveSup
end
test "as_json excludeCredentials omits transports when empty" do
credential = Struct.new(:id, :transports).new("cred-1", [])
options = ActionPack::WebAuthn::PublicKeyCredential::CreationOptions.new(
id: "user-123",
name: "user@example.com",
display_name: "Test User",
exclude_credentials: [ credential ],
exclude_credentials: [ build_credential(id: "cred-1") ],
relying_party: @relying_party
)
@@ -98,4 +95,14 @@ class ActionPack::WebAuthn::PublicKeyCredential::CreationOptionsTest < ActiveSup
{ type: "public-key", id: "cred-1" }
], options.as_json[:excludeCredentials]
end
private
def build_credential(id:, transports: [])
ActionPack::WebAuthn::PublicKeyCredential.new(
id: id,
public_key: OpenSSL::PKey::EC.generate("prime256v1"),
sign_count: 0,
transports: transports
)
end
end
@@ -3,10 +3,9 @@ require "test_helper"
class ActionPack::WebAuthn::PublicKeyCredential::RequestOptionsTest < ActiveSupport::TestCase
setup do
@relying_party = ActionPack::WebAuthn::RelyingParty.new(id: "example.com", name: "Example App")
credential = Struct.new(:id, :transports)
@credentials = [
credential.new("credential-1", []),
credential.new("credential-2", [])
build_credential(id: "credential-1"),
build_credential(id: "credential-2")
]
@options = ActionPack::WebAuthn::PublicKeyCredential::RequestOptions.new(
credentials: @credentials,
@@ -39,10 +38,9 @@ class ActionPack::WebAuthn::PublicKeyCredential::RequestOptionsTest < ActiveSupp
end
test "as_json includes transports when present" do
credential = Struct.new(:id, :transports)
credentials = [
credential.new("cred-1", [ "usb", "nfc" ]),
credential.new("cred-2", [ "internal" ])
build_credential(id: "cred-1", transports: [ "usb", "nfc" ]),
build_credential(id: "cred-2", transports: [ "internal" ])
]
options = ActionPack::WebAuthn::PublicKeyCredential::RequestOptions.new(
@@ -57,8 +55,7 @@ class ActionPack::WebAuthn::PublicKeyCredential::RequestOptionsTest < ActiveSupp
end
test "as_json omits transports when empty" do
credential = Struct.new(:id, :transports)
credentials = [ credential.new("cred-1", []) ]
credentials = [ build_credential(id: "cred-1") ]
options = ActionPack::WebAuthn::PublicKeyCredential::RequestOptions.new(
credentials: credentials,
@@ -69,4 +66,14 @@ class ActionPack::WebAuthn::PublicKeyCredential::RequestOptionsTest < ActiveSupp
{ type: "public-key", id: "cred-1" }
], options.as_json[:allowCredentials]
end
private
def build_credential(id:, transports: [])
ActionPack::WebAuthn::PublicKeyCredential.new(
id: id,
public_key: OpenSSL::PKey::EC.generate("prime256v1"),
sign_count: 0,
transports: transports
)
end
end