mirror of
https://github.com/discourse/discourse.git
synced 2026-08-11 02:59:07 +08:00
This replaces the old ember-cli build with a modern Rolldown build. In local testing, this provides an 80% improvement in build times, while remaining 100% backwards compatible for themes and plugins. As part of this move, we have decided to stop using a proxy in front of Discourse for development. Development should now be done directly against the Rails server. `bin/ember-cli -u` has been replaced with `bin/dev`. This will launch Rails on `:3000`, and will run the rolldown build in the background. Log output from both processes will be shown with an appropriate prefix. You should visit `:3000` in your browser. `:4200` will no longer serve anything. To help with migration, `bin/ember-cli` is now a backwards-compatible shim. It will print help information, and will launch a lightweight server on `:4200` with instructions to move to `:3000`. If you prefer to launch Rails and the JS build as separate commands, you can still do that. Rails boot commands are unchanged, and the rolldown development builder can be run using `bin/dev --only ember`. https://meta.discourse.org/t/403908 --------- Co-authored-by: Jarek Radosz <jarek@cvx.dev> Co-authored-by: Chris Manson <chris@manson.ie>
133 lines
3.3 KiB
Ruby
Vendored
133 lines
3.3 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
module DiscourseWebauthn
|
|
ACCEPTABLE_REGISTRATION_TYPE = "webauthn.create"
|
|
ACCEPTABLE_AUTHENTICATION_TYPE = "webauthn.get"
|
|
|
|
SUPPORTED_ALGORITHMS = [
|
|
-7, # ES256
|
|
-8, # EdDSA
|
|
-35, # ES384
|
|
-36, # ES512
|
|
-37, # PS256
|
|
-38, # PS384
|
|
-39, # PS512
|
|
-257, # RS256 (via freedom patch)
|
|
].freeze
|
|
VALID_ATTESTATION_FORMATS = %w[none packed fido-u2f].freeze
|
|
CHALLENGE_EXPIRY = 5.minutes
|
|
|
|
class SecurityKeyError < StandardError
|
|
end
|
|
|
|
class InvalidOriginError < SecurityKeyError
|
|
end
|
|
|
|
class InvalidRelyingPartyIdError < SecurityKeyError
|
|
end
|
|
|
|
class UserVerificationError < SecurityKeyError
|
|
end
|
|
|
|
class UserPresenceError < 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 KeyNotFoundError < SecurityKeyError
|
|
end
|
|
|
|
class MalformedPublicKeyCredentialError < 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.
|
|
#
|
|
# @param user [User] the user to stage the challenge for
|
|
# @param server_session [ServerSession] the session to store the challenge in
|
|
def self.stage_challenge(user, server_session)
|
|
::DiscourseWebauthn::ChallengeGenerator.generate.commit_to_session(
|
|
server_session,
|
|
user,
|
|
expires: CHALLENGE_EXPIRY,
|
|
)
|
|
end
|
|
|
|
##
|
|
# Clears the challenge from the user's server session.
|
|
#
|
|
# @param user [User] the user to clear the challenge for
|
|
# @param server_session [ServerSession] the session to clear the challenge from
|
|
def self.clear_challenge(user, server_session)
|
|
server_session.delete(session_challenge_key(user))
|
|
end
|
|
|
|
def self.allowed_credentials(user, server_session, include_passkeys: false)
|
|
has_security_keys = user.security_keys_enabled?
|
|
has_passkeys = include_passkeys && user.passkeys_for_2fa_enabled?
|
|
return {} if !has_security_keys && !has_passkeys
|
|
|
|
credential_ids = []
|
|
credential_ids.concat(user.second_factor_security_key_credential_ids) if has_security_keys
|
|
credential_ids.concat(user.passkey_credential_ids) if has_passkeys
|
|
|
|
{ allowed_credential_ids: credential_ids, challenge: challenge(user, server_session) }
|
|
end
|
|
|
|
def self.challenge(user, server_session)
|
|
server_session[session_challenge_key(user)]
|
|
end
|
|
|
|
def self.rp_id
|
|
Rails.env.production? ? Discourse.current_hostname : "localhost"
|
|
end
|
|
|
|
def self.origin
|
|
case Rails.env
|
|
when "development"
|
|
# you might need to change this and the rp_id above
|
|
# if you are using a non-default port/hostname locally
|
|
"http://localhost:3000"
|
|
else
|
|
Discourse.base_url_no_prefix
|
|
end
|
|
end
|
|
|
|
def self.rp_name
|
|
SiteSetting.title
|
|
end
|
|
|
|
def self.session_challenge_key(user)
|
|
"staged-webauthn-challenge-#{user&.id}"
|
|
end
|
|
end
|