017bcc9ce1
- Fix indentation - Split awkward initializer into separate methods - Rename credentials to passkeys - Simplify authenticator registry loading - Flatten nested errors under ActionPack::WebAuthn - Set passkey current params only requests that use it - Inline anemic methods - Replace custom validation with ActiveModel::Validation - Rename identity to holder in Passkey - Rename credentials to passkeys in JS - Extract framework library out of controllers - Pass params hashes down to ActionPack - Attempt to simplify public interface - Push data decoding down to the classes representing the data - Introduce has_passkeys - Add CBOR bigint support - Rename ActionPack::WebAuthn::Passkey to ActionPack::Passkey - Add create_passkey_button helper - Rename public-key to creation-options - Add sign_in_with_passkey_button helper - Dispatch events for the whole Passkey lifecycle - Add ED25519 support - Prevent crash on missing meta tag - Validate resident key options - Don't clobber existing ActionPack config options - Validate cryptographic params - Move CurrentWebAuthnRequest into ActionPack::Passkey - Use ActiveModel::Attributes for Options objects - Implement expiring challanges - Add lifecycle events - Extract param helpers into Request - Add passkey_creation_options and passkey_request_options helpers - Add create_passkey_challenge to make it easier to override the create method if needed - Prefix all view helpers with passkey_ - Auto-include ActionPack::Passkey::Holder - Make the passkey challange url configurable - Add a reminder about Passkeys to the magic link email
79 lines
2.8 KiB
Ruby
79 lines
2.8 KiB
Ruby
# = Action Pack WebAuthn Public Key Credential Options
|
|
#
|
|
# Abstract base class for WebAuthn ceremony options. Provides shared
|
|
# attributes and challenge generation for both CreationOptions (registration)
|
|
# and RequestOptions (authentication).
|
|
#
|
|
# This class should not be instantiated directly. Use CreationOptions or
|
|
# RequestOptions instead.
|
|
#
|
|
# == Challenge Generation
|
|
#
|
|
# Each options object generates a signed, expiring challenge via
|
|
# +ActionPack::WebAuthn.challenge_verifier+. The challenge is Base64URL-encoded
|
|
# and includes an embedded timestamp so the server can reject stale challenges.
|
|
#
|
|
# == Attributes
|
|
#
|
|
# [+user_verification+]
|
|
# Controls whether user verification (biometrics/PIN) is required. One of
|
|
# +:required+, +:preferred+, or +:discouraged+. Defaults to +:preferred+.
|
|
#
|
|
# [+relying_party+]
|
|
# The RelyingParty configuration. Defaults to +ActionPack::WebAuthn.relying_party+.
|
|
#
|
|
# [+challenge_expiration+]
|
|
# How long the challenge remains valid. Defaults vary by ceremony type
|
|
# (configured in the Railtie).
|
|
#
|
|
class ActionPack::WebAuthn::PublicKeyCredential::Options
|
|
include ActiveModel::API
|
|
include ActiveModel::Attributes
|
|
|
|
CHALLENGE_LENGTH = 32
|
|
USER_VERIFICATION_OPTIONS = %i[ required preferred discouraged ].freeze
|
|
|
|
attribute :user_verification, default: :preferred
|
|
attribute :relying_party, default: -> { ActionPack::WebAuthn.relying_party }
|
|
attribute :challenge_expiration
|
|
|
|
validates :user_verification, inclusion: { in: USER_VERIFICATION_OPTIONS }
|
|
|
|
def initialize(attributes = {})
|
|
super
|
|
self.user_verification = user_verification.to_sym
|
|
end
|
|
|
|
# Validates the options, raising +InvalidOptionsError+ if any are invalid.
|
|
def validate!
|
|
super
|
|
rescue ActiveModel::ValidationError
|
|
raise ActionPack::WebAuthn::InvalidOptionsError, errors.full_messages.to_sentence
|
|
end
|
|
|
|
# Returns a human-readable representation of the options.
|
|
def inspect
|
|
attributes_string = attributes.map { |name, value| "#{name}: #{value.inspect}" }.join(", ")
|
|
"#<#{self.class.name} #{attributes_string}>"
|
|
end
|
|
|
|
# Returns a Base64URL-encoded signed challenge containing a random nonce and
|
|
# an embedded timestamp. The challenge is generated once and memoized for the
|
|
# lifetime of this object.
|
|
#
|
|
# The timestamp allows the server to reject stale challenges. The expiration
|
|
# window is configurable per-ceremony via
|
|
# +config.action_pack.web_authn.creation_challenge_expiration+ and
|
|
# +config.action_pack.web_authn.request_challenge_expiration+, or per-instance
|
|
# via the +challenge_expiration+ attribute.
|
|
def challenge
|
|
@challenge ||= Base64.urlsafe_encode64(
|
|
ActionPack::WebAuthn.challenge_verifier.generate(
|
|
Base64.strict_encode64(SecureRandom.random_bytes(CHALLENGE_LENGTH)),
|
|
expires_in: challenge_expiration
|
|
),
|
|
padding: false
|
|
)
|
|
end
|
|
end
|