mirror of
https://github.com/we-promise/sure.git
synced 2026-05-12 23:25:00 +00:00
feat(auth): add WebAuthn MFA credentials (#1628)
* feat(auth): add WebAuthn MFA credentials * fix(auth): harden WebAuthn MFA review paths * fix(auth): polish WebAuthn error handling * fix(auth): handle duplicate WebAuthn credential races * fix(auth): permit WebAuthn credential params * fix(auth): trim WebAuthn registration controller cleanup * fix(auth): tighten WebAuthn MFA handling * fix(auth): pin WebAuthn relying party config
This commit is contained in:
@@ -10,6 +10,14 @@ class Rack::Attack
|
||||
request.ip if request.path == "/oauth/token"
|
||||
end
|
||||
|
||||
# Throttle unauthenticated WebAuthn MFA ceremonies similarly to sign-in
|
||||
# endpoints; registration remains behind normal application authentication.
|
||||
throttle("mfa/webauthn", limit: 10, period: 1.minute) do |request|
|
||||
if request.post? && request.path.in?(%w[/mfa/webauthn_options /mfa/verify_webauthn])
|
||||
request.ip
|
||||
end
|
||||
end
|
||||
|
||||
# Throttle admin endpoints to prevent brute-force attacks
|
||||
# More restrictive than general API limits since admin access is sensitive
|
||||
throttle("admin/ip", limit: 10, period: 1.minute) do |request|
|
||||
|
||||
34
config/initializers/webauthn.rb
Normal file
34
config/initializers/webauthn.rb
Normal file
@@ -0,0 +1,34 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
Rails.application.configure do
|
||||
config.x.webauthn = ActiveSupport::OrderedOptions.new
|
||||
|
||||
credentials_config = Rails.application.credentials.webauthn || {}
|
||||
credential_rp_id = credentials_config[:rp_id] || credentials_config["rp_id"]
|
||||
credential_origins = credentials_config[:allowed_origins] || credentials_config["allowed_origins"]
|
||||
|
||||
configured_rp_id = ENV["WEBAUTHN_RP_ID"].presence || credential_rp_id.presence || ENV["APP_DOMAIN"].presence
|
||||
default_rp_id = Rails.env.test? ? "www.example.com" : "localhost"
|
||||
|
||||
rp_id = configured_rp_id.presence || default_rp_id
|
||||
rp_id = rp_id.to_s.strip.sub(%r{\Ahttps?://}, "").split("/").first.to_s.split(":").first
|
||||
|
||||
configured_origins = ENV["WEBAUTHN_ALLOWED_ORIGINS"].presence || credential_origins
|
||||
allowed_origins = Array(configured_origins)
|
||||
.flat_map { |origin| origin.to_s.split(",") }
|
||||
.map { |origin| origin.strip.chomp("/") }
|
||||
.reject(&:blank?)
|
||||
|
||||
if allowed_origins.blank?
|
||||
allowed_origins = if Rails.env.test?
|
||||
[ "http://www.example.com" ]
|
||||
elsif rp_id == "localhost"
|
||||
[ "http://localhost:3000" ]
|
||||
else
|
||||
[ "https://#{rp_id}" ]
|
||||
end
|
||||
end
|
||||
|
||||
config.x.webauthn.rp_id = rp_id
|
||||
config.x.webauthn.allowed_origins = allowed_origins
|
||||
end
|
||||
@@ -31,8 +31,15 @@ en:
|
||||
verify_title: 2. Enter Verification Code
|
||||
verify:
|
||||
description: Enter the code from your authenticator app to continue
|
||||
or: or
|
||||
page_title: Verify Two-Factor Authentication
|
||||
title: Two-Factor Authentication
|
||||
verify_button: Verify
|
||||
webauthn_button: Use passkey or security key
|
||||
webauthn_unsupported: This browser does not support passkeys or security keys.
|
||||
verify_code:
|
||||
invalid_code: Invalid authentication code. Please try again.
|
||||
verify_webauthn:
|
||||
invalid_credential: Could not verify that passkey or security key. Please try again.
|
||||
webauthn_options:
|
||||
unavailable: No passkeys or security keys are available for this account.
|
||||
|
||||
@@ -10,3 +10,25 @@ en:
|
||||
mfa_description: Add an extra layer of security to your account by requiring
|
||||
a code from your authenticator app when signing in
|
||||
mfa_title: Two-Factor Authentication
|
||||
webauthn_add: Add passkey or security key
|
||||
webauthn_added: Added %{date}
|
||||
webauthn_description: Use a passkey, Touch ID, Windows Hello, or a hardware
|
||||
security key as a second factor when signing in.
|
||||
webauthn_empty: No passkeys or security keys are registered yet.
|
||||
webauthn_last_used: Last used %{time_ago} ago
|
||||
webauthn_name_label: Key name
|
||||
webauthn_name_placeholder: MacBook Touch ID, YubiKey, etc.
|
||||
webauthn_remove: Remove
|
||||
webauthn_remove_confirm: Are you sure you want to remove this passkey or
|
||||
security key?
|
||||
webauthn_remove_confirm_body: You will need to register this passkey or
|
||||
security key again before it can be used for sign-in verification.
|
||||
webauthn_title: Passkeys and security keys
|
||||
webauthn_unsupported: This browser does not support passkeys or security
|
||||
keys.
|
||||
webauthn_credentials:
|
||||
default_name: Security key
|
||||
failure: Could not save that passkey or security key. Please try again.
|
||||
mfa_required: Enable two-factor authentication before adding a passkey or security
|
||||
key.
|
||||
success: Passkey or security key removed.
|
||||
|
||||
@@ -118,6 +118,8 @@ Rails.application.routes.draw do
|
||||
resource :mfa, controller: "mfa", only: [ :new, :create ] do
|
||||
get :verify
|
||||
post :verify, to: "mfa#verify_code"
|
||||
post :webauthn_options
|
||||
post :verify_webauthn
|
||||
delete :disable
|
||||
end
|
||||
|
||||
@@ -195,6 +197,9 @@ Rails.application.routes.draw do
|
||||
end
|
||||
resource :payment, only: :show
|
||||
resource :security, only: :show
|
||||
resources :webauthn_credentials, only: %i[create destroy] do
|
||||
post :options, on: :collection
|
||||
end
|
||||
resources :sso_identities, only: :destroy
|
||||
resource :api_key, only: [ :show, :new, :create, :destroy ]
|
||||
resource :ai_prompts, only: :show
|
||||
|
||||
Reference in New Issue
Block a user