WIP: Rename Webauthn to DiscourseWebauthn (#23077)

This commit is contained in:
Penar Musaraj 2023-08-18 08:39:10 -04:00 committed by GitHub
parent 16c6ab8661
commit 10c6b2a0c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 135 additions and 88 deletions

View File

@ -344,9 +344,9 @@ class SessionController < ApplicationController
end
if matched_user&.security_keys_enabled?
Webauthn.stage_challenge(matched_user, secure_session)
DiscourseWebauthn.stage_challenge(matched_user, secure_session)
response.merge!(
Webauthn.allowed_credentials(matched_user, secure_session).merge(
DiscourseWebauthn.allowed_credentials(matched_user, secure_session).merge(
security_key_required: true,
),
)
@ -433,8 +433,8 @@ class SessionController < ApplicationController
allowed_methods: challenge[:allowed_methods],
)
if user.security_keys_enabled?
Webauthn.stage_challenge(user, secure_session)
json.merge!(Webauthn.allowed_credentials(user, secure_session))
DiscourseWebauthn.stage_challenge(user, secure_session)
json.merge!(DiscourseWebauthn.allowed_credentials(user, secure_session))
json[:security_keys_enabled] = true
else
json[:security_keys_enabled] = false
@ -660,8 +660,8 @@ class SessionController < ApplicationController
if !second_factor_authentication_result.ok
failure_payload = second_factor_authentication_result.to_h
if user.security_keys_enabled?
Webauthn.stage_challenge(user, secure_session)
failure_payload.merge!(Webauthn.allowed_credentials(user, secure_session))
DiscourseWebauthn.stage_challenge(user, secure_session)
failure_payload.merge!(DiscourseWebauthn.allowed_credentials(user, secure_session))
end
@second_factor_failure_payload = failed_json.merge(failure_payload)
return second_factor_authentication_result

View File

@ -813,11 +813,11 @@ class UsersController < ApplicationController
format.html do
return render "password_reset", layout: "no_ember" if @error
Webauthn.stage_challenge(@user, secure_session)
DiscourseWebauthn.stage_challenge(@user, secure_session)
store_preloaded(
"password_reset",
MultiJson.dump(
security_params.merge(Webauthn.allowed_credentials(@user, secure_session)),
security_params.merge(DiscourseWebauthn.allowed_credentials(@user, secure_session)),
),
)
@ -827,8 +827,9 @@ class UsersController < ApplicationController
format.json do
return render json: { message: @error } if @error
Webauthn.stage_challenge(@user, secure_session)
render json: security_params.merge(Webauthn.allowed_credentials(@user, secure_session))
DiscourseWebauthn.stage_challenge(@user, secure_session)
render json:
security_params.merge(DiscourseWebauthn.allowed_credentials(@user, secure_session))
end
end
end
@ -895,7 +896,7 @@ class UsersController < ApplicationController
format.html do
return render "password_reset", layout: "no_ember" if @error
Webauthn.stage_challenge(@user, secure_session)
DiscourseWebauthn.stage_challenge(@user, secure_session)
security_params = {
is_developer: UsernameCheckerService.is_developer?(@user.email),
@ -904,7 +905,7 @@ class UsersController < ApplicationController
security_key_required: @user.security_keys_enabled?,
backup_enabled: @user.backup_codes_enabled?,
multiple_second_factor_methods: @user.has_multiple_second_factor_methods?,
}.merge(Webauthn.allowed_credentials(@user, secure_session))
}.merge(DiscourseWebauthn.allowed_credentials(@user, secure_session))
store_preloaded("password_reset", MultiJson.dump(security_params))
@ -1545,13 +1546,13 @@ class UsersController < ApplicationController
end
def create_second_factor_security_key
challenge_session = Webauthn.stage_challenge(current_user, secure_session)
challenge_session = DiscourseWebauthn.stage_challenge(current_user, secure_session)
render json:
success_json.merge(
challenge: challenge_session.challenge,
rp_id: challenge_session.rp_id,
rp_name: challenge_session.rp_name,
supported_algorithms: ::Webauthn::SUPPORTED_ALGORITHMS,
supported_algorithms: ::DiscourseWebauthn::SUPPORTED_ALGORITHMS,
user_secure_id: current_user.create_or_fetch_secure_identifier,
existing_active_credential_ids:
current_user.second_factor_security_key_credential_ids,
@ -1563,15 +1564,15 @@ class UsersController < ApplicationController
params.require(:attestation)
params.require(:clientData)
::Webauthn::SecurityKeyRegistrationService.new(
::DiscourseWebauthn::SecurityKeyRegistrationService.new(
current_user,
params,
challenge: Webauthn.challenge(current_user, secure_session),
rp_id: Webauthn.rp_id(current_user, secure_session),
challenge: DiscourseWebauthn.challenge(current_user, secure_session),
rp_id: DiscourseWebauthn.rp_id(current_user, secure_session),
origin: Discourse.base_url,
).register_second_factor_security_key
render json: success_json
rescue ::Webauthn::SecurityKeyError => err
rescue ::DiscourseWebauthn::SecurityKeyError => err
render json: failed_json.merge(error: err.message)
end

View File

@ -127,11 +127,11 @@ class UsersEmailController < ApplicationController
else
@show_second_factor = true if @user.totp_enabled?
if @user.security_keys_enabled?
Webauthn.stage_challenge(@user, secure_session)
DiscourseWebauthn.stage_challenge(@user, secure_session)
@show_security_key = params[:show_totp].to_s == "true" ? false : true
@security_key_challenge = Webauthn.challenge(@user, secure_session)
@security_key_challenge = DiscourseWebauthn.challenge(@user, secure_session)
@security_key_allowed_credential_ids =
Webauthn.allowed_credentials(@user, secure_session)[:allowed_credential_ids]
DiscourseWebauthn.allowed_credentials(@user, secure_session)[:allowed_credential_ids]
end
end

View File

@ -146,7 +146,7 @@ module SecondFactorManager
# if we have gotten down to this point without being
# OK or invalid something has gone very weird.
invalid_second_factor_method_result
rescue ::Webauthn::SecurityKeyError => err
rescue ::DiscourseWebauthn::SecurityKeyError => err
invalid_security_key_result(err.message)
end
@ -163,11 +163,11 @@ module SecondFactorManager
end
def authenticate_security_key(secure_session, security_key_credential)
::Webauthn::SecurityKeyAuthenticationService.new(
::DiscourseWebauthn::SecurityKeyAuthenticationService.new(
self,
security_key_credential,
challenge: Webauthn.challenge(self, secure_session),
rp_id: Webauthn.rp_id(self, secure_session),
challenge: DiscourseWebauthn.challenge(self, secure_session),
rp_id: DiscourseWebauthn.rp_id(self, secure_session),
origin: Discourse.base_url,
).authenticate_security_key
end

View File

@ -4,7 +4,7 @@ require "webauthn/security_key_base_validation_service"
require "webauthn/security_key_registration_service"
require "webauthn/security_key_authentication_service"
module Webauthn
module DiscourseWebauthn
ACCEPTABLE_REGISTRATION_TYPE = "webauthn.create"
ACCEPTABLE_AUTHENTICATION_TYPE = "webauthn.get"
@ -51,7 +51,7 @@ module Webauthn
# they must respond with a valid webauthn response and
# credentials.
def self.stage_challenge(user, secure_session)
::Webauthn::ChallengeGenerator.generate.commit_to_session(secure_session, user)
::DiscourseWebauthn::ChallengeGenerator.generate.commit_to_session(secure_session, user)
end
def self.allowed_credentials(user, secure_session)
@ -60,19 +60,55 @@ module Webauthn
{
allowed_credential_ids: credential_ids,
challenge:
secure_session[Webauthn::ChallengeGenerator::ChallengeSession.session_challenge_key(user)],
secure_session[
DiscourseWebauthn::ChallengeGenerator::ChallengeSession.session_challenge_key(user)
],
}
end
def self.rp_id(user, secure_session)
secure_session[Webauthn::ChallengeGenerator::ChallengeSession.session_rp_id_key(user)]
secure_session[DiscourseWebauthn::ChallengeGenerator::ChallengeSession.session_rp_id_key(user)]
end
def self.rp_name(user, secure_session)
secure_session[Webauthn::ChallengeGenerator::ChallengeSession.session_rp_name_key(user)]
secure_session[
DiscourseWebauthn::ChallengeGenerator::ChallengeSession.session_rp_name_key(user)
]
end
def self.challenge(user, secure_session)
secure_session[Webauthn::ChallengeGenerator::ChallengeSession.session_challenge_key(user)]
secure_session[
DiscourseWebauthn::ChallengeGenerator::ChallengeSession.session_challenge_key(user)
]
end
def self.validate_first_factor_key(key)
pp key
webauthn_credential = DiscourseWebauthn::Credential.from_get(key)
p "webauthn_credential"
pp webauthn_credential
# stored_credential = user.credentials.find_by(webauthn_id: webauthn_credential.id)
# begin
# webauthn_credential.verify(
# session[:authentication_challenge],
# public_key: stored_credential.public_key,
# sign_count: stored_credential.sign_count
# )
# # Update the stored credential sign count with the value from `webauthn_credential.sign_count`
# stored_credential.update!(sign_count: webauthn_credential.sign_count)
# # Continue with successful sign in or 2FA verification...
# rescue ::WebAuthn::SignCountVerificationError => e
# # Cryptographic verification of the authenticator data succeeded, but the signature counter was less then or equal
# # to the stored value. This can have several reasons and depending on your risk tolerance you can choose to fail or
# # pass authentication. For more information see https://www.w3.org/TR/webauthn/#sign-counter
# pp e
# rescue ::WebAuthn::Error => e
# # Handle error
# end
end
end

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true
module Webauthn
module DiscourseWebauthn
class ChallengeGenerator
class ChallengeSession
attr_reader :challenge, :rp_id, :rp_name

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
require "cose"
module Webauthn
module DiscourseWebauthn
class SecurityKeyAuthenticationService < SecurityKeyBaseValidationService
##
# See https://w3c.github.io/webauthn/#sctn-verifying-assertion for
@ -30,7 +30,7 @@ module Webauthn
client_data
# 8. Verify that the value of C.type is the string webauthn.get.
validate_webauthn_type(::Webauthn::ACCEPTABLE_AUTHENTICATION_TYPE)
validate_webauthn_type(::DiscourseWebauthn::ACCEPTABLE_AUTHENTICATION_TYPE)
# 9. Verify that the value of C.challenge equals the base64url encoding of options.challenge.
validate_challenge

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
module Webauthn
module DiscourseWebauthn
class SecurityKeyBaseValidationService
def initialize(current_user, params, challenge_params)
@current_user = current_user

View File

@ -2,7 +2,7 @@
require "cbor"
require "cose"
module Webauthn
module DiscourseWebauthn
class SecurityKeyRegistrationService < SecurityKeyBaseValidationService
##
# See https://w3c.github.io/webauthn/#sctn-registering-a-new-credential for
@ -10,7 +10,7 @@ module Webauthn
# place in the step flow to make the process clearer.
def register_second_factor_security_key
# 4. Verify that the value of C.type is webauthn.create.
validate_webauthn_type(::Webauthn::ACCEPTABLE_REGISTRATION_TYPE)
validate_webauthn_type(::DiscourseWebauthn::ACCEPTABLE_REGISTRATION_TYPE)
# 5. Verify that the value of C.challenge equals the base64url encoding of options.challenge.
validate_challenge
@ -51,7 +51,7 @@ module Webauthn
# codes.
credential_public_key, credential_public_key_bytes, credential_id =
extract_public_key_and_credential_from_attestation(auth_data)
if ::Webauthn::SUPPORTED_ALGORITHMS.exclude?(credential_public_key.alg)
if ::DiscourseWebauthn::SUPPORTED_ALGORITHMS.exclude?(credential_public_key.alg)
raise(
UnsupportedPublicKeyAlgorithmError,
I18n.t("webauthn.validation.unsupported_public_key_algorithm_error"),
@ -72,7 +72,7 @@ module Webauthn
# name [WebAuthn-Registries].
# 16. Verify that attStmt is a correct attestation statement, conveying a valid attestation signature,
# by using the attestation statement format fmts verification procedure given attStmt, authData and hash.
if ::Webauthn::VALID_ATTESTATION_FORMATS.exclude?(attestation["fmt"]) ||
if ::DiscourseWebauthn::VALID_ATTESTATION_FORMATS.exclude?(attestation["fmt"]) ||
attestation["fmt"] != "none"
raise(
UnsupportedAttestationFormatError,

View File

@ -182,7 +182,7 @@ RSpec.describe SecondFactorManager do
before do
disable_totp
simulate_localhost_webauthn_challenge
Webauthn.stage_challenge(user, secure_session)
DiscourseWebauthn.stage_challenge(user, secure_session)
end
context "when security key params are valid" do
@ -264,7 +264,7 @@ RSpec.describe SecondFactorManager do
before do
simulate_localhost_webauthn_challenge
Webauthn.stage_challenge(user, secure_session)
DiscourseWebauthn.stage_challenge(user, secure_session)
end
context "when method selected is invalid" do
@ -312,7 +312,7 @@ RSpec.describe SecondFactorManager do
before do
simulate_localhost_webauthn_challenge
Webauthn.stage_challenge(user, secure_session)
DiscourseWebauthn.stage_challenge(user, secure_session)
end
context "when security key params are valid" do

View File

@ -1,9 +1,9 @@
# frozen_string_literal: true
RSpec.describe Webauthn::ChallengeGenerator do
it "generates a Webauthn::ChallengeGenerator::ChallengeSession with correct params" do
session = Webauthn::ChallengeGenerator.generate
expect(session).to be_a(Webauthn::ChallengeGenerator::ChallengeSession)
RSpec.describe DiscourseWebauthn::ChallengeGenerator do
it "generates a DiscourseWebauthn::ChallengeGenerator::ChallengeSession with correct params" do
session = DiscourseWebauthn::ChallengeGenerator.generate
expect(session).to be_a(DiscourseWebauthn::ChallengeGenerator::ChallengeSession)
expect(session.challenge).not_to eq(nil)
expect(session.rp_id).to eq(Discourse.current_hostname)
expect(session.rp_name).to eq(SiteSetting.title)
@ -15,7 +15,7 @@ RSpec.describe Webauthn::ChallengeGenerator do
it "stores the challenge, rp id, and rp name in the provided session object" do
secure_session = {}
generated_session = Webauthn::ChallengeGenerator.generate
generated_session = DiscourseWebauthn::ChallengeGenerator.generate
generated_session.commit_to_session(secure_session, user)
expect(secure_session["staged-webauthn-challenge-#{user&.id}"]).to eq(

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true
require "webauthn"
require "discourse_webauthn"
require "webauthn/security_key_registration_service"
##
@ -37,7 +37,7 @@ require "webauthn/security_key_registration_service"
#
# The origin params just need to be whatever your localhost URL for Discourse is.
RSpec.describe Webauthn::SecurityKeyAuthenticationService do
RSpec.describe DiscourseWebauthn::SecurityKeyAuthenticationService do
subject(:service) { described_class.new(current_user, params, challenge_params) }
let(:security_key_user) { current_user }
@ -118,7 +118,7 @@ RSpec.describe Webauthn::SecurityKeyAuthenticationService do
it "raises a NotFoundError" do
expect { service.authenticate_security_key }.to raise_error(
Webauthn::NotFoundError,
DiscourseWebauthn::NotFoundError,
I18n.t("webauthn.validation.not_found_error"),
)
end
@ -129,7 +129,7 @@ RSpec.describe Webauthn::SecurityKeyAuthenticationService do
it "raises an OwnershipError" do
expect { service.authenticate_security_key }.to raise_error(
Webauthn::OwnershipError,
DiscourseWebauthn::OwnershipError,
I18n.t("webauthn.validation.ownership_error"),
)
end
@ -140,7 +140,7 @@ RSpec.describe Webauthn::SecurityKeyAuthenticationService do
it "raises an InvalidTypeError" do
expect { service.authenticate_security_key }.to raise_error(
Webauthn::InvalidTypeError,
DiscourseWebauthn::InvalidTypeError,
I18n.t("webauthn.validation.invalid_type_error"),
)
end
@ -151,7 +151,7 @@ RSpec.describe Webauthn::SecurityKeyAuthenticationService do
it "raises a ChallengeMismatchError" do
expect { service.authenticate_security_key }.to raise_error(
Webauthn::ChallengeMismatchError,
DiscourseWebauthn::ChallengeMismatchError,
I18n.t("webauthn.validation.challenge_mismatch_error"),
)
end
@ -162,7 +162,7 @@ RSpec.describe Webauthn::SecurityKeyAuthenticationService do
it "raises a InvalidOriginError" do
expect { service.authenticate_security_key }.to raise_error(
Webauthn::InvalidOriginError,
DiscourseWebauthn::InvalidOriginError,
I18n.t("webauthn.validation.invalid_origin_error"),
)
end
@ -173,7 +173,7 @@ RSpec.describe Webauthn::SecurityKeyAuthenticationService do
it "raises a InvalidRelyingPartyIdError" do
expect { service.authenticate_security_key }.to raise_error(
Webauthn::InvalidRelyingPartyIdError,
DiscourseWebauthn::InvalidRelyingPartyIdError,
I18n.t("webauthn.validation.invalid_relying_party_id_error"),
)
end
@ -184,7 +184,7 @@ RSpec.describe Webauthn::SecurityKeyAuthenticationService do
it "raises a PublicKeyError" do
expect { service.authenticate_security_key }.to raise_error(
Webauthn::PublicKeyError,
DiscourseWebauthn::PublicKeyError,
I18n.t("webauthn.validation.public_key_error"),
)
end
@ -195,7 +195,7 @@ RSpec.describe Webauthn::SecurityKeyAuthenticationService do
it "raises a UnknownCOSEAlgorithmError" do
expect { service.authenticate_security_key }.to raise_error(
Webauthn::UnknownCOSEAlgorithmError,
DiscourseWebauthn::UnknownCOSEAlgorithmError,
I18n.t("webauthn.validation.unknown_cose_algorithm_error"),
)
end
@ -237,6 +237,8 @@ RSpec.describe Webauthn::SecurityKeyAuthenticationService do
end
it "all supported algorithms are implemented" do
Webauthn::SUPPORTED_ALGORITHMS.each { |alg| expect(COSE::Algorithm.find(alg)).not_to be_nil }
DiscourseWebauthn::SUPPORTED_ALGORITHMS.each do |alg|
expect(COSE::Algorithm.find(alg)).not_to be_nil
end
end
end

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true
require "webauthn"
require "discourse_webauthn"
require "webauthn/security_key_registration_service"
RSpec.describe Webauthn::SecurityKeyRegistrationService do
RSpec.describe DiscourseWebauthn::SecurityKeyRegistrationService do
subject(:service) { described_class.new(current_user, params, challenge_params) }
let(:client_data_challenge) { Base64.encode64(challenge) }
@ -42,7 +42,7 @@ RSpec.describe Webauthn::SecurityKeyRegistrationService do
it "raises an InvalidTypeError" do
expect { service.register_second_factor_security_key }.to raise_error(
Webauthn::InvalidTypeError,
DiscourseWebauthn::InvalidTypeError,
I18n.t("webauthn.validation.invalid_type_error"),
)
end
@ -53,7 +53,7 @@ RSpec.describe Webauthn::SecurityKeyRegistrationService do
it "raises a ChallengeMismatchError" do
expect { service.register_second_factor_security_key }.to raise_error(
Webauthn::ChallengeMismatchError,
DiscourseWebauthn::ChallengeMismatchError,
I18n.t("webauthn.validation.challenge_mismatch_error"),
)
end
@ -64,7 +64,7 @@ RSpec.describe Webauthn::SecurityKeyRegistrationService do
it "raises a InvalidOriginError" do
expect { service.register_second_factor_security_key }.to raise_error(
Webauthn::InvalidOriginError,
DiscourseWebauthn::InvalidOriginError,
I18n.t("webauthn.validation.invalid_origin_error"),
)
end
@ -75,7 +75,7 @@ RSpec.describe Webauthn::SecurityKeyRegistrationService do
it "raises a InvalidRelyingPartyIdError" do
expect { service.register_second_factor_security_key }.to raise_error(
Webauthn::InvalidRelyingPartyIdError,
DiscourseWebauthn::InvalidRelyingPartyIdError,
I18n.t("webauthn.validation.invalid_relying_party_id_error"),
)
end
@ -83,35 +83,39 @@ RSpec.describe Webauthn::SecurityKeyRegistrationService do
context "when the public key algorithm is not supported by the server" do
before do
@original_supported_alg_value = Webauthn::SUPPORTED_ALGORITHMS
silence_warnings { Webauthn::SUPPORTED_ALGORITHMS = [-999] }
@original_supported_alg_value = DiscourseWebauthn::SUPPORTED_ALGORITHMS
silence_warnings { DiscourseWebauthn::SUPPORTED_ALGORITHMS = [-999] }
end
it "raises a UnsupportedPublicKeyAlgorithmError" do
expect { service.register_second_factor_security_key }.to raise_error(
Webauthn::UnsupportedPublicKeyAlgorithmError,
DiscourseWebauthn::UnsupportedPublicKeyAlgorithmError,
I18n.t("webauthn.validation.unsupported_public_key_algorithm_error"),
)
end
after { silence_warnings { Webauthn::SUPPORTED_ALGORITHMS = @original_supported_alg_value } }
after do
silence_warnings { DiscourseWebauthn::SUPPORTED_ALGORITHMS = @original_supported_alg_value }
end
end
context "when the attestation format is not supported" do
before do
@original_supported_alg_value = Webauthn::VALID_ATTESTATION_FORMATS
silence_warnings { Webauthn::VALID_ATTESTATION_FORMATS = ["err"] }
@original_supported_alg_value = DiscourseWebauthn::VALID_ATTESTATION_FORMATS
silence_warnings { DiscourseWebauthn::VALID_ATTESTATION_FORMATS = ["err"] }
end
it "raises a UnsupportedAttestationFormatError" do
expect { service.register_second_factor_security_key }.to raise_error(
Webauthn::UnsupportedAttestationFormatError,
DiscourseWebauthn::UnsupportedAttestationFormatError,
I18n.t("webauthn.validation.unsupported_attestation_format_error"),
)
end
after do
silence_warnings { Webauthn::VALID_ATTESTATION_FORMATS = @original_supported_alg_value }
silence_warnings do
DiscourseWebauthn::VALID_ATTESTATION_FORMATS = @original_supported_alg_value
end
end
end
@ -126,7 +130,7 @@ RSpec.describe Webauthn::SecurityKeyRegistrationService do
# error!
expect { service.register_second_factor_security_key }.to raise_error(
Webauthn::CredentialIdInUseError,
DiscourseWebauthn::CredentialIdInUseError,
I18n.t("webauthn.validation.credential_id_in_use_error"),
)
end
@ -139,7 +143,7 @@ RSpec.describe Webauthn::SecurityKeyRegistrationService do
it "raises a MalformedAttestationError" do
expect { service.register_second_factor_security_key }.to raise_error(
Webauthn::MalformedAttestationError,
DiscourseWebauthn::MalformedAttestationError,
I18n.t("webauthn.validation.malformed_attestation_error"),
)
end

View File

@ -182,7 +182,7 @@ RSpec.configure do |config|
config.include RSpecHtmlMatchers
config.include IntegrationHelpers, type: :request
config.include SystemHelpers, type: :system
config.include WebauthnIntegrationHelpers
config.include DiscourseWebauthnIntegrationHelpers
config.include SiteSettingsHelpers
config.include SidekiqHelpers
config.include UploadsHelpers

View File

@ -117,8 +117,10 @@ RSpec.describe SessionController do
[user_security_key.credential_id],
)
secure_session = SecureSession.new(session["secure_session_id"])
expect(response_body_parsed["challenge"]).to eq(Webauthn.challenge(user, secure_session))
expect(Webauthn.rp_id(user, secure_session)).to eq(Discourse.current_hostname)
expect(response_body_parsed["challenge"]).to eq(
DiscourseWebauthn.challenge(user, secure_session),
)
expect(DiscourseWebauthn.rp_id(user, secure_session)).to eq(Discourse.current_hostname)
end
end
end

View File

@ -459,8 +459,8 @@ RSpec.describe UsersController do
it "stages a webauthn challenge and rp-id for the user" do
secure_session = SecureSession.new(session["secure_session_id"])
expect(Webauthn.challenge(user1, secure_session)).not_to eq(nil)
expect(Webauthn.rp_id(user1, secure_session)).to eq(Discourse.current_hostname)
expect(DiscourseWebauthn.challenge(user1, secure_session)).not_to eq(nil)
expect(DiscourseWebauthn.rp_id(user1, secure_session)).to eq(Discourse.current_hostname)
end
it "changes password with valid security key challenge and authentication" do
@ -5658,13 +5658,15 @@ RSpec.describe UsersController do
create_second_factor_security_key
secure_session = read_secure_session
response_parsed = response.parsed_body
expect(response_parsed["challenge"]).to eq(Webauthn.challenge(user1, secure_session))
expect(response_parsed["rp_id"]).to eq(Webauthn.rp_id(user1, secure_session))
expect(response_parsed["rp_name"]).to eq(Webauthn.rp_name(user1, secure_session))
expect(response_parsed["challenge"]).to eq(DiscourseWebauthn.challenge(user1, secure_session))
expect(response_parsed["rp_id"]).to eq(DiscourseWebauthn.rp_id(user1, secure_session))
expect(response_parsed["rp_name"]).to eq(DiscourseWebauthn.rp_name(user1, secure_session))
expect(response_parsed["user_secure_id"]).to eq(
user1.reload.create_or_fetch_secure_identifier,
)
expect(response_parsed["supported_algorithms"]).to eq(::Webauthn::SUPPORTED_ALGORITHMS)
expect(response_parsed["supported_algorithms"]).to eq(
::DiscourseWebauthn::SUPPORTED_ALGORITHMS,
)
end
context "if the user has security key credentials already" do

View File

@ -1,6 +1,6 @@
# frozen_string_literal: true
module WebauthnIntegrationHelpers
module DiscourseWebauthnIntegrationHelpers
##
# Usage notes:
#
@ -10,8 +10,8 @@ module WebauthnIntegrationHelpers
#
# To make this all work together you need to
# create a UserSecurityKey for a user using valid_security_key_data,
# and you override Webauthn::ChallengeGenerator.generate to return
# a Webauthn::ChallengeGenerator::ChallengeSession object using
# and you override DiscourseWebauthn::ChallengeGenerator.generate to return
# a DiscourseWebauthn::ChallengeGenerator::ChallengeSession object using
# valid_security_key_challenge_data.
#
# This is because the challenge is embedded
@ -64,8 +64,8 @@ module WebauthnIntegrationHelpers
def simulate_localhost_webauthn_challenge
stub_as_dev_localhost
Webauthn::ChallengeGenerator.stubs(:generate).returns(
Webauthn::ChallengeGenerator::ChallengeSession.new(
DiscourseWebauthn::ChallengeGenerator.stubs(:generate).returns(
DiscourseWebauthn::ChallengeGenerator::ChallengeSession.new(
challenge: valid_security_key_challenge_data[:challenge],
rp_id: Discourse.current_hostname,
),