2019-10-02 10:08:41 +08:00
|
|
|
# frozen_string_literal: true
|
2020-01-10 08:45:56 +08:00
|
|
|
require "webauthn/challenge_generator"
|
2019-10-02 10:08:41 +08:00
|
|
|
require "webauthn/security_key_base_validation_service"
|
|
|
|
require "webauthn/security_key_registration_service"
|
|
|
|
require "webauthn/security_key_authentication_service"
|
|
|
|
|
2023-08-18 20:39:10 +08:00
|
|
|
module DiscourseWebauthn
|
2020-04-30 14:48:34 +08:00
|
|
|
ACCEPTABLE_REGISTRATION_TYPE = "webauthn.create"
|
|
|
|
ACCEPTABLE_AUTHENTICATION_TYPE = "webauthn.get"
|
2019-11-21 13:45:36 +08:00
|
|
|
|
|
|
|
# -7 - ES256
|
|
|
|
# -257 - RS256 (Windows Hello supported alg.)
|
2022-02-08 20:07:47 +08:00
|
|
|
SUPPORTED_ALGORITHMS = COSE::Algorithm.registered_algorithm_ids.freeze
|
2019-10-02 10:08:41 +08:00
|
|
|
VALID_ATTESTATION_FORMATS = %w[none packed fido-u2f].freeze
|
|
|
|
|
|
|
|
class SecurityKeyError < StandardError
|
|
|
|
end
|
|
|
|
|
|
|
|
class InvalidOriginError < SecurityKeyError
|
|
|
|
end
|
|
|
|
class InvalidRelyingPartyIdError < SecurityKeyError
|
|
|
|
end
|
|
|
|
class UserVerificationError < SecurityKeyError
|
|
|
|
end
|
|
|
|
class ChallengeMismatchError < SecurityKeyError
|
|
|
|
end
|
|
|
|
class InvalidTypeError < SecurityKeyError
|
|
|
|
end
|
|
|
|
class UnsupportedPublicKeyAlgorithmError < SecurityKeyError
|
|
|
|
end
|
|
|
|
class UnsupportedAttestationFormatError < SecurityKeyError
|
|
|
|
end
|
|
|
|
class CredentialIdInUseError < SecurityKeyError
|
|
|
|
end
|
|
|
|
class MalformedAttestationError < SecurityKeyError
|
|
|
|
end
|
|
|
|
class NotFoundError < SecurityKeyError
|
|
|
|
end
|
|
|
|
class OwnershipError < SecurityKeyError
|
|
|
|
end
|
|
|
|
class PublicKeyError < SecurityKeyError
|
|
|
|
end
|
2020-01-02 08:14:22 +08:00
|
|
|
class UnknownCOSEAlgorithmError < SecurityKeyError
|
|
|
|
end
|
2020-01-10 08:45:56 +08:00
|
|
|
|
|
|
|
##
|
|
|
|
# Usage:
|
|
|
|
#
|
|
|
|
# These methods should be used in controllers where we
|
|
|
|
# are challenging the user that has a security key, and
|
|
|
|
# they must respond with a valid webauthn response and
|
|
|
|
# credentials.
|
|
|
|
def self.stage_challenge(user, secure_session)
|
2023-08-18 20:39:10 +08:00
|
|
|
::DiscourseWebauthn::ChallengeGenerator.generate.commit_to_session(secure_session, user)
|
2020-01-10 08:45:56 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.allowed_credentials(user, secure_session)
|
|
|
|
return {} if !user.security_keys_enabled?
|
|
|
|
credential_ids = user.second_factor_security_key_credential_ids
|
|
|
|
{
|
|
|
|
allowed_credential_ids: credential_ids,
|
2023-08-31 21:11:23 +08:00
|
|
|
challenge: secure_session[self.session_challenge_key(user)],
|
2020-01-10 08:45:56 +08:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2023-08-31 21:11:23 +08:00
|
|
|
def self.challenge(user, secure_session)
|
|
|
|
secure_session[self.session_challenge_key(user)]
|
2020-01-10 08:45:56 +08:00
|
|
|
end
|
|
|
|
|
2023-08-31 21:11:23 +08:00
|
|
|
def self.rp_id
|
|
|
|
Discourse.current_hostname
|
2020-01-10 08:45:56 +08:00
|
|
|
end
|
|
|
|
|
2023-08-31 21:11:23 +08:00
|
|
|
def self.rp_name
|
|
|
|
SiteSetting.title
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.session_challenge_key(user)
|
|
|
|
"staged-webauthn-challenge-#{user&.id}"
|
2023-08-18 20:39:10 +08:00
|
|
|
end
|
2019-10-02 10:08:41 +08:00
|
|
|
end
|