Add passkey model layer
Related to #569927
What does this MR do and why?
Adds the passkey model layer
- Adds the
:authentication_mode
,:passkey_eligible
, and:last_used_at
columns with appropriate validation in theWebauthnRegistration
table - Connects to the
User
model via associations - Updates the
WebauthnRegistration
factory
Database Review
Query Plans (Backwards-compatibility)
Table
has_many | Query Plan |
---|---|
Initial (:webauthn_registrations) | link |
Post-migration (:webauthn_registrations) | link |
Post-migration (:passkeys) | link |
Local Migrations
bin/rails db:migrate
main: == [advisory_lock_connection] object_id: 156460, pg_backend_pid: 63025
main: == 20250926104422 AddPasskeyColumnsToWebauthnRegistrations: migrating =========
main: -- add_column(:webauthn_registrations, :authentication_mode, :integer, {:default=>1, :null=>false})
main: -> 0.0483s
main: -- add_column(:webauthn_registrations, :passkey_eligible, :boolean, {:default=>false, :null=>false})
main: -> 0.0023s
main: -- add_column(:webauthn_registrations, :last_used_at, :datetime_with_timezone)
main: -> 0.0071s
main: == 20250926104422 AddPasskeyColumnsToWebauthnRegistrations: migrated (0.0631s)
main: == [advisory_lock_connection] object_id: 156460, pg_backend_pid: 63025
ci: == [advisory_lock_connection] object_id: 156460, pg_backend_pid: 63026
ci: == 20250926104422 AddPasskeyColumnsToWebauthnRegistrations: migrating =========
ci: -- add_column(:webauthn_registrations, :authentication_mode, :integer, {:default=>1, :null=>false})
ci: -> 0.0086s
ci: -- add_column(:webauthn_registrations, :passkey_eligible, :boolean, {:default=>false, :null=>false})
ci: -> 0.0028s
ci: -- add_column(:webauthn_registrations, :last_used_at, :datetime_with_timezone)
ci: -> 0.0012s
ci: == 20250926104422 AddPasskeyColumnsToWebauthnRegistrations: migrated (0.0233s)
ci: == [advisory_lock_connection] object_id: 156460, pg_backend_pid: 63026
VERSION=20250926104422 bin/rails db:migrate:down:main && VERSION=20250926104422 bin/rails db:migrate:down:ci
main: == [advisory_lock_connection] object_id: 156160, pg_backend_pid: 64613
main: == 20250926104422 AddPasskeyColumnsToWebauthnRegistrations: reverting =========
main: -- remove_column(:webauthn_registrations, :authentication_mode)
main: -> 0.0413s
main: -- remove_column(:webauthn_registrations, :passkey_eligible)
main: -> 0.0020s
main: -- remove_column(:webauthn_registrations, :last_used_at)
main: -> 0.0022s
main: == 20250926104422 AddPasskeyColumnsToWebauthnRegistrations: reverted (0.0521s)
main: == [advisory_lock_connection] object_id: 156160, pg_backend_pid: 64613
ci: == [advisory_lock_connection] object_id: 156160, pg_backend_pid: 65283
ci: == 20250926104422 AddPasskeyColumnsToWebauthnRegistrations: reverting =========
ci: -- remove_column(:webauthn_registrations, :authentication_mode)
ci: -> 0.0559s
ci: -- remove_column(:webauthn_registrations, :passkey_eligible)
ci: -> 0.0021s
ci: -- remove_column(:webauthn_registrations, :last_used_at)
ci: -> 0.0020s
ci: == 20250926104422 AddPasskeyColumnsToWebauthnRegistrations: reverted (0.0775s)
ci: == [advisory_lock_connection] object_id: 156160, pg_backend_pid: 65283
How to set up and validate locally
- Clone this branch & run migrations
bin/rails db:migrate
- Open a rails console
gdk rails console
- Create a user with a WebAuthn registration and verify model changes.
user = User.first
auth1 = user.webauthn_registrations.create!(
name: '1Password',
credential_xid: SecureRandom.hex,
public_key: SecureRandom.hex,
counter: 0
)
user.webauthn_registrations.size
user.passkeys.size
auth1.update!(authentication_mode: 'passwordless')
user.webauthn_registrations.size
user.passkeys.size
auth1.last_used_at
auth1.update_last_used_at!
auth1.last_used_at
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Edited by Hakeem Abdul-Razak