0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-11 02:59:07 +08:00
discourse/lib/discourse_webauthn.rb
David Taylor 9527868295
DEV: Replace JS build system with Rolldown (#35963)
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>
2026-05-29 11:11:55 +01:00

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