discourse/app/models/concerns
Rafael dos Santos Silva 0c90e25e47
DEV: Split passkey and security key WebAuthn ceremonies for 2FA (#40817)
Passkeys used as 2FA (behind `allow_passkeys_for_2fa`) previously shared
a single WebAuthn ceremony with second-factor security keys on
`/session/2fa`: one merged credential allow-list, posted as the
`security_key` method, with `userVerification: "preferred"`. A single
ceremony cannot both require user verification for passkeys and accept
legacy non-UV security keys, so this splits them:

* New `passkey` value (4) in `UserSecondFactor.methods` carries the
ceremony intent on the wire. No rows ever store it; passkeys live in
`user_security_keys`.
* `DiscourseWebauthn.allowed_credentials` now returns
`allowed_credential_ids` (second-factor keys only, as before the
combined ceremony) plus a separate `passkey_allowed_credential_ids`.
* `authenticate_security_key` only accepts second-factor credentials
again; the new `authenticate_passkey` only accepts first-factor
credentials. A passkey assertion posted to the security key ceremony (or
vice versa) fails with an ownership error.
* The `/session/2fa` page shows distinct "Use passkey" (UV required) and
"Use security key" (UV discouraged) actions instead of one mixed button.
* `passkeys_for_2fa_enabled?` is renamed to
`passkeys_available_as_second_factor?` (old name kept as an alias) and
now ignores disabled passkey rows.

No behavior expansion: passkeys still only satisfy `/session/2fa`.

This is the first of three stacked PRs completing the
`allow_passkeys_for_2fa` rollout so passkeys count as valid 2FA
everywhere, including `enforce_second_factor`. The safe ordering is:
every login/recovery path must be able to *challenge* a passkey before
any path starts *trusting* passkey-only accounts as compliant.
2026-06-17 12:52:47 -03:00
..
reports PERF: Improve trust level pipeline report (#40948) 2026-06-17 10:43:13 +08:00
anon_cache_invalidator.rb
cached_counting.rb DEV: Upgrade the Redis gem to v5.4 2025-03-19 14:34:00 +01:00
category_hashtag.rb DEV: Replace Ruby numbered parameters by it where applicable (#37810) 2026-02-13 13:59:07 +01:00
custom_field.rb
has_custom_fields.rb DEV: Enable some minor rubocop rules (#40094) 2026-05-19 15:29:38 +02:00
has_deprecated_columns.rb DEV: Remove version-number-based logic (#25482) 2024-01-30 17:34:10 +00:00
has_destroyed_web_hook.rb DEV: Enable some minor rubocop rules (#40094) 2026-05-19 15:29:38 +02:00
has_nested_reply_stats.rb FEATURE: Allow editing a post's reply target from the composer (#39471) 2026-04-29 12:23:26 -03:00
has_post_upload_references.rb DEV: Enable Style/RedundantSelf rubocop rule (#40098) 2026-05-19 19:27:45 +02:00
has_sanitizable_fields.rb
has_search_data.rb DEV: Enable Style/RedundantSelf rubocop rule (#40098) 2026-05-19 19:27:45 +02:00
has_url.rb DEV: Enable Style/RedundantSelf rubocop rule (#40098) 2026-05-19 19:27:45 +02:00
limited_edit.rb FIX: Always trust admin and moderators with post edits (#25602) 2024-02-08 13:10:26 +10:00
locale_matchable.rb DEV: Use localizable concerns across post, topic, categories (#34137) 2025-08-07 22:12:01 +08:00
localizable.rb FEATURE: Localize local oneboxes (#40493) 2026-06-08 11:49:55 +08:00
positionable.rb DEV: Enable Style/RedundantParentheses rubocop rule (#40095) 2026-05-19 15:48:09 +02:00
reviewable_action_builder.rb DEV: Enable Style/RedundantSelf rubocop rule (#40098) 2026-05-19 19:27:45 +02:00
roleable.rb FIX: Moderators can enable plugins when enable_category_type_setup SiteSetting is enabled (#40349) 2026-06-17 08:37:14 -05:00
searchable.rb DEV: Enable Style/RedundantSelf rubocop rule (#40098) 2026-05-19 19:27:45 +02:00
second_factor_manager.rb DEV: Split passkey and security key WebAuthn ceremonies for 2FA (#40817) 2026-06-17 12:52:47 -03:00
stats_cacheable.rb
topic_tracking_state_publishable.rb
trashable.rb DEV: Enable Style/RedundantSelf rubocop rule (#40098) 2026-05-19 19:27:45 +02:00