StandardRB fixes
This commit is contained in:
@@ -4,7 +4,7 @@ class WebauthnController < ApplicationController
|
||||
|
||||
# Rate limit check endpoint to prevent enumeration attacks
|
||||
rate_limit to: 10, within: 1.minute, only: [:check], with: -> {
|
||||
render json: { error: "Too many requests. Try again later." }, status: :too_many_requests
|
||||
render json: {error: "Too many requests. Try again later."}, status: :too_many_requests
|
||||
}
|
||||
|
||||
# GET /webauthn/new
|
||||
@@ -16,7 +16,7 @@ class WebauthnController < ApplicationController
|
||||
# Generate registration challenge for creating a new passkey
|
||||
def challenge
|
||||
user = Current.session&.user
|
||||
return render json: { error: "Not authenticated" }, status: :unauthorized unless user
|
||||
return render json: {error: "Not authenticated"}, status: :unauthorized unless user
|
||||
|
||||
registration_options = WebAuthn::Credential.options_for_create(
|
||||
user: {
|
||||
@@ -44,7 +44,7 @@ class WebauthnController < ApplicationController
|
||||
credential_data, nickname = extract_credential_params
|
||||
|
||||
if credential_data.blank? || nickname.blank?
|
||||
render json: { error: "Credential and nickname are required" }, status: :unprocessable_entity
|
||||
render json: {error: "Credential and nickname are required"}, status: :unprocessable_entity
|
||||
return
|
||||
end
|
||||
|
||||
@@ -52,7 +52,7 @@ class WebauthnController < ApplicationController
|
||||
challenge = session.delete(:webauthn_challenge)
|
||||
|
||||
if challenge.blank?
|
||||
render json: { error: "Invalid or expired session" }, status: :unprocessable_entity
|
||||
render json: {error: "Invalid or expired session"}, status: :unprocessable_entity
|
||||
return
|
||||
end
|
||||
|
||||
@@ -68,10 +68,10 @@ class WebauthnController < ApplicationController
|
||||
client_extension_results = response["clientExtensionResults"] || {}
|
||||
|
||||
authenticator_type = if response["response"]["authenticatorAttachment"] == "cross-platform"
|
||||
"cross-platform"
|
||||
else
|
||||
"platform"
|
||||
end
|
||||
"cross-platform"
|
||||
else
|
||||
"platform"
|
||||
end
|
||||
|
||||
# Determine if this is a backup/synced credential
|
||||
backup_eligible = client_extension_results["credProps"]&.dig("rk") || false
|
||||
@@ -79,7 +79,7 @@ class WebauthnController < ApplicationController
|
||||
|
||||
# Store the credential
|
||||
user = Current.session&.user
|
||||
return render json: { error: "Not authenticated" }, status: :unauthorized unless user
|
||||
return render json: {error: "Not authenticated"}, status: :unauthorized unless user
|
||||
|
||||
@webauthn_credential = user.webauthn_credentials.create!(
|
||||
external_id: Base64.urlsafe_encode64(webauthn_credential.id),
|
||||
@@ -96,13 +96,12 @@ class WebauthnController < ApplicationController
|
||||
message: "Passkey '#{nickname}' registered successfully",
|
||||
credential_id: @webauthn_credential.id
|
||||
}
|
||||
|
||||
rescue WebAuthn::Error => e
|
||||
Rails.logger.error "WebAuthn registration error: #{e.message}"
|
||||
render json: { error: "Failed to register passkey: #{e.message}" }, status: :unprocessable_entity
|
||||
render json: {error: "Failed to register passkey: #{e.message}"}, status: :unprocessable_entity
|
||||
rescue => e
|
||||
Rails.logger.error "Unexpected WebAuthn registration error: #{e.class} - #{e.message}"
|
||||
render json: { error: "An unexpected error occurred" }, status: :internal_server_error
|
||||
render json: {error: "An unexpected error occurred"}, status: :internal_server_error
|
||||
end
|
||||
end
|
||||
|
||||
@@ -115,7 +114,7 @@ class WebauthnController < ApplicationController
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
redirect_to profile_path,
|
||||
notice: "Passkey '#{nickname}' has been removed"
|
||||
notice: "Passkey '#{nickname}' has been removed"
|
||||
}
|
||||
format.json {
|
||||
render json: {
|
||||
@@ -133,7 +132,7 @@ class WebauthnController < ApplicationController
|
||||
email = params[:email]&.strip&.downcase
|
||||
|
||||
if email.blank?
|
||||
render json: { has_webauthn: false, requires_webauthn: false }
|
||||
render json: {has_webauthn: false, requires_webauthn: false}
|
||||
return
|
||||
end
|
||||
|
||||
@@ -142,7 +141,7 @@ class WebauthnController < ApplicationController
|
||||
# Security: Return identical response for non-existent users
|
||||
# Combined with rate limiting (10/min), this prevents account enumeration
|
||||
if user.nil?
|
||||
render json: { has_webauthn: false, requires_webauthn: false }
|
||||
render json: {has_webauthn: false, requires_webauthn: false}
|
||||
return
|
||||
end
|
||||
|
||||
@@ -158,37 +157,36 @@ class WebauthnController < ApplicationController
|
||||
def extract_credential_params
|
||||
# Use require.permit which is working and reliable
|
||||
# The JavaScript sends params both directly and wrapped in webauthn key
|
||||
begin
|
||||
# Try direct parameters first
|
||||
credential_params = params.require(:credential).permit(:id, :rawId, :type, response: {}, clientExtensionResults: {})
|
||||
nickname = params.require(:nickname)
|
||||
[credential_params, nickname]
|
||||
rescue ActionController::ParameterMissing
|
||||
Rails.logger.error("Using the fallback parameters")
|
||||
# Fallback to webauthn-wrapped parameters
|
||||
webauthn_params = params.require(:webauthn).permit(:nickname, credential: [:id, :rawId, :type, response: {}, clientExtensionResults: {}])
|
||||
[webauthn_params[:credential], webauthn_params[:nickname]]
|
||||
end
|
||||
|
||||
# Try direct parameters first
|
||||
credential_params = params.require(:credential).permit(:id, :rawId, :type, response: {}, clientExtensionResults: {})
|
||||
nickname = params.require(:nickname)
|
||||
[credential_params, nickname]
|
||||
rescue ActionController::ParameterMissing
|
||||
Rails.logger.error("Using the fallback parameters")
|
||||
# Fallback to webauthn-wrapped parameters
|
||||
webauthn_params = params.require(:webauthn).permit(:nickname, credential: [:id, :rawId, :type, response: {}, clientExtensionResults: {}])
|
||||
[webauthn_params[:credential], webauthn_params[:nickname]]
|
||||
end
|
||||
|
||||
def set_webauthn_credential
|
||||
user = Current.session&.user
|
||||
return render json: { error: "Not authenticated" }, status: :unauthorized unless user
|
||||
return render json: {error: "Not authenticated"}, status: :unauthorized unless user
|
||||
@webauthn_credential = user.webauthn_credentials.find(params[:id])
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
respond_to do |format|
|
||||
format.html { redirect_to profile_path, alert: "Passkey not found" }
|
||||
format.json { render json: { error: "Passkey not found" }, status: :not_found }
|
||||
format.json { render json: {error: "Passkey not found"}, status: :not_found }
|
||||
end
|
||||
end
|
||||
|
||||
# Helper method to convert Base64 to Base64URL if needed
|
||||
def base64_to_base64url(str)
|
||||
str.gsub('+', '-').gsub('/', '_').gsub(/=+$/, '')
|
||||
str.tr("+", "-").tr("/", "_").gsub(/=+$/, "")
|
||||
end
|
||||
|
||||
# Helper method to convert Base64URL to Base64 if needed
|
||||
def base64url_to_base64(str)
|
||||
str.gsub('-', '+').gsub('_', '/') + '=' * (4 - str.length % 4) % 4
|
||||
str.tr("-", "+").tr("_", "/") + "=" * (4 - str.length % 4) % 4
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user