# frozen_string_literal: true require "webauthn/challenge_generator" require "webauthn/security_key_base_validation_service" require "webauthn/security_key_registration_service" require "webauthn/security_key_authentication_service" module DiscourseWebauthn ACCEPTABLE_REGISTRATION_TYPE = "webauthn.create" ACCEPTABLE_AUTHENTICATION_TYPE = "webauthn.get" # -7 - ES256 # -257 - RS256 (Windows Hello supported alg.) SUPPORTED_ALGORITHMS = COSE::Algorithm.registered_algorithm_ids.freeze 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 class UnknownCOSEAlgorithmError < SecurityKeyError end ## # 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) ::DiscourseWebauthn::ChallengeGenerator.generate.commit_to_session(secure_session, user) 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, challenge: secure_session[self.session_challenge_key(user)], } end def self.challenge(user, secure_session) secure_session[self.session_challenge_key(user)] end def self.rp_id Discourse.current_hostname end def self.rp_name SiteSetting.title end def self.session_challenge_key(user) "staged-webauthn-challenge-#{user&.id}" end end