Skip to content

Commit

Permalink
feat: allow multiple origins set per RelyingParty
Browse files Browse the repository at this point in the history
* add a possibility to set `allowed_origins` configuration option that would be an alternative to `origin`

* update Readme
  • Loading branch information
obroshnij committed Sep 9, 2024
1 parent f030a78 commit e0261d0
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 125 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ WebAuthn.configure do |config|
# the User Agent during registration and authentication ceremonies.
config.origin = "https://auth.example.com"

# For the case when a relying party has multiple origins (e.g. different subdomains, native client sending android:apk-key-hash:... like origins in clientDataJson, etc...), you can set the allowed origins instead of the single origin above
# config.allowed_origins = [
# "https://auth.example.com",
# "android:apk-key-hash:blablablablablalblalla"
# ]
# Note: in this case setting config.rp_id is mandatory

# Relying Party name for display purposes
config.rp_name = "Example Inc."

Expand Down
24 changes: 22 additions & 2 deletions lib/webauthn/authenticator_response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def initialize(client_data_json:, relying_party: WebAuthn.configuration.relying_
end

def verify(expected_challenge, expected_origin = nil, user_verification: nil, rp_id: nil)
expected_origin ||= relying_party.origin || raise("Unspecified expected origin")
expected_origin ||= relying_party.origin || relying_party.allowed_origins || raise("Unspecified expected origin")
rp_id ||= relying_party.id

verify_item(:type)
Expand Down Expand Up @@ -82,11 +82,26 @@ def valid_challenge?(expected_challenge)
OpenSSL.secure_compare(client_data.challenge, expected_challenge)
end

# @return [Boolean]
# @param [String, Array<String>] expected_origin
# Validate if origin configured for RP is matching the one received from client
def valid_origin?(expected_origin)
expected_origin && (client_data.origin == expected_origin)
return false unless expected_origin

case expected_origin
when Array
expected_origin.include?(client_data.origin) # allow multiple origins as per spec
else
client_data.origin == expected_origin # keep the default behaviour for backwards compatibility
end
end

# @return [Boolean]
# @param [String] rp_id
# Validate if RP ID is matching the one received from client
def valid_rp_id?(rp_id)
return false unless rp_id

OpenSSL::Digest::SHA256.digest(rp_id) == authenticator_data.rp_id_hash
end

Expand All @@ -104,7 +119,12 @@ def valid_user_verified?
authenticator_data.user_verified?
end

# @return [String, nil]
# Extract RP ID from origin in case rp_id is not provided explicitly
# Note: In case origin is an array, we can not guess anymore since any guess would end up being wrong
def rp_id_from_origin(expected_origin)
return if expected_origin.is_a?(Array)

URI.parse(expected_origin).host
end

Expand Down
10 changes: 4 additions & 6 deletions lib/webauthn/client_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,10 @@ def hash

def data
@data ||=
begin
if client_data_json
JSON.parse(client_data_json)
else
raise ClientDataMissingError, "Client Data JSON is missing"
end
if client_data_json
JSON.parse(client_data_json)
else
raise ClientDataMissingError, "Client Data JSON is missing"
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/webauthn/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class Configuration
:encoding=,
:origin,
:origin=,
:allowed_origins,
:allowed_origins=,
:verify_attestation_statement,
:verify_attestation_statement=,
:credential_options_timeout,
Expand Down
7 changes: 5 additions & 2 deletions lib/webauthn/relying_party.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ module WebAuthn
class RootCertificateFinderNotSupportedError < Error; end

class RelyingParty
DEFAULT_ALGORITHMS = ["ES256", "PS256", "RS256"].compact.freeze

def self.if_pss_supported(algorithm)
OpenSSL::PKey::RSA.instance_methods.include?(:verify_pss) ? algorithm : nil
end

DEFAULT_ALGORITHMS = ["ES256", "PS256", "RS256"].compact.freeze

def initialize(
algorithms: DEFAULT_ALGORITHMS.dup,
encoding: WebAuthn::Encoder::STANDARD_ENCODING,
allowed_origins: nil,
origin: nil,
id: nil,
name: nil,
Expand All @@ -31,6 +32,7 @@ def initialize(
@algorithms = algorithms
@encoding = encoding
@origin = origin
@allowed_origins = allowed_origins.nil? ? [origin] : allowed_origins
@id = id
@name = name
@verify_attestation_statement = verify_attestation_statement
Expand All @@ -43,6 +45,7 @@ def initialize(

attr_accessor :algorithms,
:encoding,
:allowed_origins,
:origin,
:id,
:name,
Expand Down
2 changes: 1 addition & 1 deletion lib/webauthn/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module WebAuthn
VERSION = "3.1.0"
VERSION = "3.2.0"
end
Loading

0 comments on commit e0261d0

Please sign in to comment.