webauthn 3.3.0 → 3.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 758cb181dfb381a05748cee3143da113a66e40433b329ff5711e0ef973c799ec
4
- data.tar.gz: 06df9d255ea57db6b9874c1406ab5f5606108a18abe4c992b799b7b5db8191be
3
+ metadata.gz: 93072800aa309dfef7946c9dd7f1601b41c8b6b11de8142aa388fb1f19b62869
4
+ data.tar.gz: 76bdba0f89cbcd48f30692af6ba50eb558dab98f07e4e8c7913d6a60a70ef582
5
5
  SHA512:
6
- metadata.gz: d502b7a87d0472fab4c7e254438b5d6029702f3bf88ecd71244dae92c6789fd70d618b0422278498a5d4012aee3f9728cc378821c4d280f41140c9d695851530
7
- data.tar.gz: 5f9851c90114474b0aa19129af023ea781255aa7baf8fc9ceb3873d6cd9a0fc12fbec3f10b57aa1c2c7486a34a48762fae4bfd06ab8f85fea95dfa40d936298d
6
+ metadata.gz: 7801c6840c5f4287724887ee2646ee1aa7966ffe8da3cf8f7a2b014564448f33a68b8d12011a87f8445bf661b6c55cca0a5d7309fe44411ad4b249ff5ceff01e
7
+ data.tar.gz: d25993a7b2868a3f3445af99b388ac68ef67a406a73f997b99f690304506d75f46a06b22c99aeeeb105271b849e86f855c11f610fd2908ea9b2f44a3f245060a
@@ -15,12 +15,12 @@ on:
15
15
 
16
16
  jobs:
17
17
  test:
18
- runs-on: ubuntu-20.04
18
+ runs-on: ubuntu-24.04
19
19
  strategy:
20
20
  fail-fast: false
21
21
  matrix:
22
22
  ruby:
23
- - '3.4.0-preview2'
23
+ - '3.4'
24
24
  - '3.3'
25
25
  - '3.2'
26
26
  - '3.1'
data/CHANGELOG.md CHANGED
@@ -1,10 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.4.1] - 2025-06-06
4
+
5
+ - Avoid requiring `base64` as it's not a direct dependency. [#459](https://github.com/cedarcode/webauthn-ruby/pull/459)[@santiagorodriguez96]
6
+
7
+ ## [v3.4.0] - 2025-02-17
8
+
9
+ - Added support for Webauthn.config and RelayingParty to accept multiple allowed_origins. [#431](https://github.com/cedarcode/webauthn-ruby/pull/431)[@obroshnij]
10
+
3
11
  ## [v3.3.0] - 2025-02-06
4
12
 
5
13
  ### Added
6
14
 
7
- - Updated `tpm-key_attestation` dependency from `~> 1.12.0` to `~> 1.14.0`. [#449](https://github.com/cedarcode/webauthn-ruby/pull/449) [@brauliomartinezlm], [@nicolastemciuc]
15
+ - Updated `tpm-key_attestation` dependency from `~> 0.12.0` to `~> 0.14.0`. [#449](https://github.com/cedarcode/webauthn-ruby/pull/449) [@brauliomartinezlm], [@nicolastemciuc]
8
16
 
9
17
  ## [v3.2.2] - 2024-11-14
10
18
 
@@ -413,6 +421,12 @@ Note: Both additions should help making it compatible with Chrome for Android 70
413
421
  - `WebAuthn::AuthenticatorAttestationResponse.valid?` can be used to validate fido-u2f attestations returned by the browser
414
422
  - Works with ruby 2.5
415
423
 
424
+ [v3.4.1]: https://github.com/cedarcode/webauthn-ruby/compare/v3.4.0...v3.4.1/
425
+ [v3.4.0]: https://github.com/cedarcode/webauthn-ruby/compare/v3.3.0...v3.4.0/
426
+ [v3.3.0]: https://github.com/cedarcode/webauthn-ruby/compare/v3.2.2...v3.3.0/
427
+ [v3.2.2]: https://github.com/cedarcode/webauthn-ruby/compare/v3.2.1...v3.2.2/
428
+ [v3.2.1]: https://github.com/cedarcode/webauthn-ruby/compare/v3.2.0...v3.2.1/
429
+ [v3.2.0]: https://github.com/cedarcode/webauthn-ruby/compare/v3.1.0...v3.2.0/
416
430
  [v3.1.0]: https://github.com/cedarcode/webauthn-ruby/compare/v3.0.0...v3.1.0/
417
431
  [v3.0.0]: https://github.com/cedarcode/webauthn-ruby/compare/2-stable...v3.0.0/
418
432
  [v3.0.0.alpha2]: https://github.com/cedarcode/webauthn-ruby/compare/2-stable...v3.0.0.alpha2/
data/README.md CHANGED
@@ -104,7 +104,8 @@ For a Rails application this would go in `config/initializers/webauthn.rb`.
104
104
  WebAuthn.configure do |config|
105
105
  # This value needs to match `window.location.origin` evaluated by
106
106
  # the User Agent during registration and authentication ceremonies.
107
- config.origin = "https://auth.example.com"
107
+ # Multiple origins can be used when needed. Using more than one will imply you MUST configure rp_id explicitely. If you need your credentials to be bound to a single origin but you have more than one tenant, please see [our Advanced Configuration section](https://github.com/cedarcode/webauthn-ruby/blob/master/docs/advanced_configuration.md) instead of adding multiple origins.
108
+ config.allowed_origins = ["https://auth.example.com"]
108
109
 
109
110
  # Relying Party name for display purposes
110
111
  config.rp_name = "Example Inc."
@@ -46,7 +46,7 @@ module WebAuthn
46
46
  end
47
47
 
48
48
  def attestation_certificate_key_id
49
- attestation_certificate.subject_key_identifier&.unpack("H*")&.[](0)
49
+ attestation_certificate.subject_key_identifier&.unpack1("H*")
50
50
  end
51
51
 
52
52
  private
@@ -25,7 +25,8 @@ module WebAuthn
25
25
  end
26
26
 
27
27
  def verify(expected_challenge, expected_origin = nil, user_presence: nil, user_verification: nil, rp_id: nil)
28
- expected_origin ||= relying_party.origin || raise("Unspecified expected origin")
28
+ expected_origin ||= relying_party.allowed_origins || raise("Unspecified expected origin")
29
+
29
30
  rp_id ||= relying_party.id
30
31
 
31
32
  verify_item(:type)
@@ -33,7 +34,11 @@ module WebAuthn
33
34
  verify_item(:challenge, expected_challenge)
34
35
  verify_item(:origin, expected_origin)
35
36
  verify_item(:authenticator_data)
36
- verify_item(:rp_id, rp_id || rp_id_from_origin(expected_origin))
37
+
38
+ verify_item(
39
+ :rp_id,
40
+ rp_id || rp_id_from_origin(expected_origin)
41
+ )
37
42
 
38
43
  # Fallback to RP configuration unless user_presence is passed in explicitely
39
44
  if user_presence.nil? && !relying_party.silent_authentication || user_presence
@@ -84,10 +89,14 @@ module WebAuthn
84
89
  end
85
90
 
86
91
  def valid_origin?(expected_origin)
87
- expected_origin && (client_data.origin == expected_origin)
92
+ return false unless expected_origin
93
+
94
+ expected_origin.include?(client_data.origin)
88
95
  end
89
96
 
90
97
  def valid_rp_id?(rp_id)
98
+ return false unless rp_id
99
+
91
100
  OpenSSL::Digest::SHA256.digest(rp_id) == authenticator_data.rp_id_hash
92
101
  end
93
102
 
@@ -106,7 +115,7 @@ module WebAuthn
106
115
  end
107
116
 
108
117
  def rp_id_from_origin(expected_origin)
109
- URI.parse(expected_origin).host
118
+ URI.parse(expected_origin.first).host if expected_origin.size == 1
110
119
  end
111
120
 
112
121
  def type
@@ -49,12 +49,10 @@ module WebAuthn
49
49
 
50
50
  def data
51
51
  @data ||=
52
- begin
53
- if client_data_json
54
- JSON.parse(client_data_json)
55
- else
56
- raise ClientDataMissingError, "Client Data JSON is missing"
57
- end
52
+ if client_data_json
53
+ JSON.parse(client_data_json)
54
+ else
55
+ raise ClientDataMissingError, "Client Data JSON is missing"
58
56
  end
59
57
  end
60
58
  end
@@ -22,6 +22,8 @@ module WebAuthn
22
22
  :encoding=,
23
23
  :origin,
24
24
  :origin=,
25
+ :allowed_origins,
26
+ :allowed_origins=,
25
27
  :verify_attestation_statement,
26
28
  :verify_attestation_statement=,
27
29
  :credential_options_timeout,
@@ -1,55 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "base64"
3
+ require "webauthn/encoders"
4
4
 
5
5
  module WebAuthn
6
- def self.standard_encoder
7
- @standard_encoder ||= Encoder.new
8
- end
9
-
10
6
  class Encoder
7
+ extend Forwardable
8
+
11
9
  # https://www.w3.org/TR/webauthn-2/#base64url-encoding
12
10
  STANDARD_ENCODING = :base64url
13
11
 
14
- attr_reader :encoding
12
+ def_delegators :@encoder_klass, :encode, :decode
15
13
 
16
14
  def initialize(encoding = STANDARD_ENCODING)
17
- @encoding = encoding
18
- end
19
-
20
- def encode(data)
21
- case encoding
22
- when :base64
23
- [data].pack("m0") # Base64.strict_encode64(data)
24
- when :base64url
25
- data = [data].pack("m0") # Base64.urlsafe_encode64(data, padding: false)
26
- data.chomp!("==") or data.chomp!("=")
27
- data.tr!("+/", "-_")
28
- data
29
- when nil, false
30
- data
31
- else
32
- raise "Unsupported or unknown encoding: #{encoding}"
33
- end
34
- end
35
-
36
- def decode(data)
37
- case encoding
38
- when :base64
39
- data.unpack1("m0") # Base64.strict_decode64(data)
40
- when :base64url
41
- if !data.end_with?("=") && data.length % 4 != 0 # Base64.urlsafe_decode64(data)
42
- data = data.ljust((data.length + 3) & ~3, "=")
43
- data.tr!("-_", "+/")
44
- else
45
- data = data.tr("-_", "+/")
46
- end
47
- data.unpack1("m0")
48
- when nil, false
49
- data
50
- else
51
- raise "Unsupported or unknown encoding: #{encoding}"
52
- end
15
+ @encoder_klass = Encoders.lookup(encoding)
53
16
  end
54
17
  end
55
18
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WebAuthn
4
+ def self.standard_encoder
5
+ @standard_encoder ||= Encoders.lookup(Encoder::STANDARD_ENCODING)
6
+ end
7
+
8
+ module Encoders
9
+ class << self
10
+ def lookup(encoding)
11
+ case encoding
12
+ when :base64
13
+ Base64Encoder
14
+ when :base64url
15
+ Base64UrlEncoder
16
+ when nil, false
17
+ NullEncoder
18
+ else
19
+ raise "Unsupported or unknown encoding: #{encoding}"
20
+ end
21
+ end
22
+ end
23
+
24
+ class Base64Encoder
25
+ def self.encode(data)
26
+ [data].pack("m0") # Base64.strict_encode64(data)
27
+ end
28
+
29
+ def self.decode(data)
30
+ data.unpack1("m0") # Base64.strict_decode64(data)
31
+ end
32
+ end
33
+
34
+ class Base64UrlEncoder
35
+ def self.encode(data)
36
+ data = [data].pack("m0") # Base64.urlsafe_encode64(data, padding: false)
37
+ data.chomp!("==") or data.chomp!("=")
38
+ data.tr!("+/", "-_")
39
+ data
40
+ end
41
+
42
+ def self.decode(data)
43
+ if !data.end_with?("=") && data.length % 4 != 0 # Base64.urlsafe_decode64(data)
44
+ data = data.ljust((data.length + 3) & ~3, "=")
45
+ end
46
+
47
+ data = data.tr("-_", "+/")
48
+ data.unpack1("m0")
49
+ end
50
+ end
51
+
52
+ class NullEncoder
53
+ def self.encode(data)
54
+ data
55
+ end
56
+
57
+ def self.decode(data)
58
+ data
59
+ end
60
+ end
61
+ end
62
+ end
@@ -9,15 +9,16 @@ module WebAuthn
9
9
  class RootCertificateFinderNotSupportedError < Error; end
10
10
 
11
11
  class RelyingParty
12
+ DEFAULT_ALGORITHMS = ["ES256", "PS256", "RS256"].compact.freeze
13
+
12
14
  def self.if_pss_supported(algorithm)
13
15
  OpenSSL::PKey::RSA.instance_methods.include?(:verify_pss) ? algorithm : nil
14
16
  end
15
17
 
16
- DEFAULT_ALGORITHMS = ["ES256", "PS256", "RS256"].compact.freeze
17
-
18
18
  def initialize(
19
19
  algorithms: DEFAULT_ALGORITHMS.dup,
20
20
  encoding: WebAuthn::Encoder::STANDARD_ENCODING,
21
+ allowed_origins: nil,
21
22
  origin: nil,
22
23
  id: nil,
23
24
  name: nil,
@@ -30,7 +31,7 @@ module WebAuthn
30
31
  )
31
32
  @algorithms = algorithms
32
33
  @encoding = encoding
33
- @origin = origin
34
+ @allowed_origins = allowed_origins
34
35
  @id = id
35
36
  @name = name
36
37
  @verify_attestation_statement = verify_attestation_statement
@@ -38,12 +39,13 @@ module WebAuthn
38
39
  @silent_authentication = silent_authentication
39
40
  @acceptable_attestation_types = acceptable_attestation_types
40
41
  @legacy_u2f_appid = legacy_u2f_appid
42
+ self.origin = origin
41
43
  self.attestation_root_certificates_finders = attestation_root_certificates_finders
42
44
  end
43
45
 
44
46
  attr_accessor :algorithms,
45
47
  :encoding,
46
- :origin,
48
+ :allowed_origins,
47
49
  :id,
48
50
  :name,
49
51
  :verify_attestation_statement,
@@ -52,7 +54,7 @@ module WebAuthn
52
54
  :acceptable_attestation_types,
53
55
  :legacy_u2f_appid
54
56
 
55
- attr_reader :attestation_root_certificates_finders
57
+ attr_reader :attestation_root_certificates_finders, :origin
56
58
 
57
59
  # This is the user-data encoder.
58
60
  # Used to decode user input and to encode data provided to the user.
@@ -118,5 +120,18 @@ module WebAuthn
118
120
  block_given? ? [webauthn_credential, stored_credential] : webauthn_credential
119
121
  end
120
122
  end
123
+
124
+ # DEPRECATED: This method will be removed in future.
125
+ def origin=(new_origin)
126
+ return if new_origin.nil?
127
+
128
+ warn(
129
+ "DEPRECATION WARNING: `WebAuthn.origin` is deprecated and will be removed in future. "\
130
+ "Please use `WebAuthn.allowed_origins` instead "\
131
+ "that also allows configuring multiple origins per Relying Party"
132
+ )
133
+
134
+ @allowed_origins ||= Array(new_origin) # rubocop:disable Naming/MemoizedInstanceVariableName
135
+ end
121
136
  end
122
137
  end
@@ -43,7 +43,9 @@ module WebAuthn
43
43
  end
44
44
 
45
45
  def attestation_trust_path
46
- @attestation_trust_path ||= [OpenSSL::X509::Certificate.new(Base64.strict_decode64(@certificate))]
46
+ @attestation_trust_path ||= [
47
+ OpenSSL::X509::Certificate.new(WebAuthn::Encoders::Base64Encoder.decode(@certificate))
48
+ ]
47
49
  end
48
50
 
49
51
  private
@@ -51,14 +53,14 @@ module WebAuthn
51
53
  # https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#u2f-authenticatorMakeCredential-interoperability
52
54
  # Let credentialId be a credentialIdLength byte array initialized with CTAP1/U2F response key handle bytes.
53
55
  def credential_id
54
- Base64.urlsafe_decode64(@key_handle)
56
+ WebAuthn::Encoders::Base64UrlEncoder.decode(@key_handle)
55
57
  end
56
58
 
57
59
  # Let x9encodedUserPublicKey be the user public key returned in the U2F registration response message [U2FRawMsgs].
58
60
  # Let coseEncodedCredentialPublicKey be the result of converting x9encodedUserPublicKey’s value from ANS X9.62 /
59
61
  # Sec-1 v2 uncompressed curve point representation [SEC1V2] to COSE_Key representation ([RFC8152] Section 7).
60
62
  def credential_cose_key
61
- decoded_public_key = Base64.strict_decode64(@public_key)
63
+ decoded_public_key = WebAuthn::Encoders::Base64Encoder.decode(@public_key)
62
64
  if WebAuthn::AttestationStatement::FidoU2f::PublicKey.uncompressed_point?(decoded_public_key)
63
65
  COSE::Key::EC2.new(
64
66
  alg: COSE::Algorithm.by_name("ES256").id,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WebAuthn
4
- VERSION = "3.3.0"
4
+ VERSION = "3.4.1"
5
5
  end
data/webauthn.gemspec CHANGED
@@ -41,12 +41,11 @@ Gem::Specification.new do |spec|
41
41
  spec.add_dependency "safety_net_attestation", "~> 0.4.0"
42
42
  spec.add_dependency "tpm-key_attestation", "~> 0.14.0"
43
43
 
44
- spec.add_development_dependency "base64", ">= 0.1.0"
45
44
  spec.add_development_dependency "bundler", ">= 1.17", "< 3.0"
46
45
  spec.add_development_dependency "byebug", "~> 11.0"
47
46
  spec.add_development_dependency "rake", "~> 13.0"
48
47
  spec.add_development_dependency "rspec", "~> 3.8"
49
- spec.add_development_dependency "rubocop", "~> 1.9.1"
50
- spec.add_development_dependency "rubocop-rake", "~> 0.5.1"
51
- spec.add_development_dependency "rubocop-rspec", "~> 2.2.0"
48
+ spec.add_development_dependency "rubocop", "~> 1"
49
+ spec.add_development_dependency "rubocop-rake", "~> 0.5"
50
+ spec.add_development_dependency "rubocop-rspec", ">= 2.2", "< 4.0"
52
51
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webauthn
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.0
4
+ version: 3.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gonzalo Rodriguez
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2025-02-06 00:00:00.000000000 Z
12
+ date: 2025-06-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: android_key_attestation
@@ -109,20 +109,6 @@ dependencies:
109
109
  - - "~>"
110
110
  - !ruby/object:Gem::Version
111
111
  version: 0.14.0
112
- - !ruby/object:Gem::Dependency
113
- name: base64
114
- requirement: !ruby/object:Gem::Requirement
115
- requirements:
116
- - - ">="
117
- - !ruby/object:Gem::Version
118
- version: 0.1.0
119
- type: :development
120
- prerelease: false
121
- version_requirements: !ruby/object:Gem::Requirement
122
- requirements:
123
- - - ">="
124
- - !ruby/object:Gem::Version
125
- version: 0.1.0
126
112
  - !ruby/object:Gem::Dependency
127
113
  name: bundler
128
114
  requirement: !ruby/object:Gem::Requirement
@@ -191,42 +177,48 @@ dependencies:
191
177
  requirements:
192
178
  - - "~>"
193
179
  - !ruby/object:Gem::Version
194
- version: 1.9.1
180
+ version: '1'
195
181
  type: :development
196
182
  prerelease: false
197
183
  version_requirements: !ruby/object:Gem::Requirement
198
184
  requirements:
199
185
  - - "~>"
200
186
  - !ruby/object:Gem::Version
201
- version: 1.9.1
187
+ version: '1'
202
188
  - !ruby/object:Gem::Dependency
203
189
  name: rubocop-rake
204
190
  requirement: !ruby/object:Gem::Requirement
205
191
  requirements:
206
192
  - - "~>"
207
193
  - !ruby/object:Gem::Version
208
- version: 0.5.1
194
+ version: '0.5'
209
195
  type: :development
210
196
  prerelease: false
211
197
  version_requirements: !ruby/object:Gem::Requirement
212
198
  requirements:
213
199
  - - "~>"
214
200
  - !ruby/object:Gem::Version
215
- version: 0.5.1
201
+ version: '0.5'
216
202
  - !ruby/object:Gem::Dependency
217
203
  name: rubocop-rspec
218
204
  requirement: !ruby/object:Gem::Requirement
219
205
  requirements:
220
- - - "~>"
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '2.2'
209
+ - - "<"
221
210
  - !ruby/object:Gem::Version
222
- version: 2.2.0
211
+ version: '4.0'
223
212
  type: :development
224
213
  prerelease: false
225
214
  version_requirements: !ruby/object:Gem::Requirement
226
215
  requirements:
227
- - - "~>"
216
+ - - ">="
217
+ - !ruby/object:Gem::Version
218
+ version: '2.2'
219
+ - - "<"
228
220
  - !ruby/object:Gem::Version
229
- version: 2.2.0
221
+ version: '4.0'
230
222
  description: |-
231
223
  WebAuthn ruby server library ― Make your application a W3C Web Authentication conformant
232
224
  Relying Party and allow your users to authenticate with U2F and FIDO2 authenticators.
@@ -282,6 +274,7 @@ files:
282
274
  - lib/webauthn/credential_rp_entity.rb
283
275
  - lib/webauthn/credential_user_entity.rb
284
276
  - lib/webauthn/encoder.rb
277
+ - lib/webauthn/encoders.rb
285
278
  - lib/webauthn/error.rb
286
279
  - lib/webauthn/fake_authenticator.rb
287
280
  - lib/webauthn/fake_authenticator/attestation_object.rb