webauthn 2.3.0 → 3.0.0.alpha1

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: 4094023fc463d77a38548e294121f819e874bfb1c075ca43b1fb38e41cfd53a2
4
- data.tar.gz: f410f8d7e000822943be953265a32e8f81423908e6aff282486d5afa4ab62eb4
3
+ metadata.gz: 1f418eb52a085d8e7c03bd1c3d11b88ed8fe467f4ec01d3178836689d470f436
4
+ data.tar.gz: 4ab67e8804cbd7d785e29b94760af6db9d6b1e52de1a58bafc74aed19b5b7e21
5
5
  SHA512:
6
- metadata.gz: 23ea57e2264cc45024174e8d7a54bc3d4f373cca916c4453079d5cfccf46caa4dbc5aa4013a54404121274a35e71f97f90a061062a21ca270a8b58d474345fb8
7
- data.tar.gz: 3b49c5b5b845fdcfc3b0b647a16aeab07ad219210e8e29835d171e387d6bcc4ac5fe08a4f9d2978ee6db1022363d1c2b06fe0a163a8625038aca51e1d274e903
6
+ metadata.gz: ccdcd22e494079eb67c122c03e3061166f91de50dd0c2bf8662748c976483a9d472fa9f8d6b67db9abf484e8eac182195b5f1e1cca8aaaf88146d831919f1c93
7
+ data.tar.gz: b2506b530c796ee57e5d037e7dd3692b1e359408fd9757141b6c4f042c43d57344baa09dc63d17ace549927eeea50abaa8c0771a0ab6da0ece5d880362d890db
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.0.0.alpha1] - 2020-06-27
4
+
5
+ ### Added
6
+
7
+ - Ability to define multiple relying parties with the introduction of the `WebAuthn::RelyingParty` class ([@padulafacundo], [@brauliomartinezlm])
8
+
3
9
  ## [v2.3.0] - 2020-06-27
4
10
 
5
11
  ### Added
@@ -294,6 +300,7 @@ Note: Both additions should help making it compatible with Chrome for Android 70
294
300
  - `WebAuthn::AuthenticatorAttestationResponse.valid?` can be used to validate fido-u2f attestations returned by the browser
295
301
  - Works with ruby 2.5
296
302
 
303
+ [v3.0.0.alpha1]: https://github.com/cedarcode/webauthn-ruby/compare/2-stable...v3.0.0.alpha1/
297
304
  [v2.3.0]: https://github.com/cedarcode/webauthn-ruby/compare/v2.2.1...v2.3.0/
298
305
  [v2.2.1]: https://github.com/cedarcode/webauthn-ruby/compare/v2.2.0...v2.2.1/
299
306
  [v2.2.0]: https://github.com/cedarcode/webauthn-ruby/compare/v2.1.0...v2.2.0/
@@ -321,6 +328,7 @@ Note: Both additions should help making it compatible with Chrome for Android 70
321
328
  [v0.2.0]: https://github.com/cedarcode/webauthn-ruby/compare/v0.1.0...v0.2.0/
322
329
  [v0.1.0]: https://github.com/cedarcode/webauthn-ruby/compare/v0.0.0...v0.1.0/
323
330
 
331
+ [@brauliomartinezlm]: https://github.com/brauliomartinezlm
324
332
  [@bdewater]: https://github.com/bdewater
325
333
  [@jdongelmans]: https://github.com/jdongelmans
326
334
  [@kalebtesfay]: https://github.com/kalebtesfay
@@ -10,18 +10,22 @@ module WebAuthn
10
10
  class AttestationObject
11
11
  extend Forwardable
12
12
 
13
- def self.deserialize(attestation_object)
14
- from_map(CBOR.decode(attestation_object))
13
+ def self.deserialize(attestation_object, relying_party)
14
+ from_map(CBOR.decode(attestation_object), relying_party)
15
15
  end
16
16
 
17
- def self.from_map(map)
17
+ def self.from_map(map, relying_party)
18
18
  new(
19
19
  authenticator_data: WebAuthn::AuthenticatorData.deserialize(map["authData"]),
20
- attestation_statement: WebAuthn::AttestationStatement.from(map["fmt"], map["attStmt"])
20
+ attestation_statement: WebAuthn::AttestationStatement.from(
21
+ map["fmt"],
22
+ map["attStmt"],
23
+ relying_party: relying_party
24
+ )
21
25
  )
22
26
  end
23
27
 
24
- attr_reader :authenticator_data, :attestation_statement
28
+ attr_reader :authenticator_data, :attestation_statement, :relying_party
25
29
 
26
30
  def initialize(authenticator_data:, attestation_statement:)
27
31
  @authenticator_data = authenticator_data
@@ -28,11 +28,11 @@ module WebAuthn
28
28
  ATTESTATION_FORMAT_TPM => WebAuthn::AttestationStatement::TPM
29
29
  }.freeze
30
30
 
31
- def self.from(format, statement)
31
+ def self.from(format, statement, relying_party: WebAuthn.configuration.relying_party)
32
32
  klass = FORMAT_TO_CLASS[format]
33
33
 
34
34
  if klass
35
- klass.new(statement)
35
+ klass.new(statement, relying_party)
36
36
  else
37
37
  raise(FormatNotSupportedError, "Unsupported attestation format '#{format}'")
38
38
  end
@@ -26,8 +26,9 @@ module WebAuthn
26
26
  class Base
27
27
  AAGUID_EXTENSION_OID = "1.3.6.1.4.1.45724.1.1.4"
28
28
 
29
- def initialize(statement)
29
+ def initialize(statement, relying_party = WebAuthn.configuration.relying_party)
30
30
  @statement = statement
31
+ @relying_party = relying_party
31
32
  end
32
33
 
33
34
  def valid?(_authenticator_data, _client_data_hash)
@@ -54,7 +55,7 @@ module WebAuthn
54
55
 
55
56
  private
56
57
 
57
- attr_reader :statement
58
+ attr_reader :statement, :relying_party
58
59
 
59
60
  def matching_aaguid?(attested_credential_data_aaguid)
60
61
  extension = attestation_certificate&.extensions&.detect { |ext| ext.oid == AAGUID_EXTENSION_OID }
@@ -95,10 +96,10 @@ module WebAuthn
95
96
 
96
97
  def trustworthy?(aaguid: nil, attestation_certificate_key_id: nil)
97
98
  if ATTESTATION_TYPES_WITH_ROOT.include?(attestation_type)
98
- configuration.acceptable_attestation_types.include?(attestation_type) &&
99
+ relying_party.acceptable_attestation_types.include?(attestation_type) &&
99
100
  valid_certificate_chain?(aaguid: aaguid, attestation_certificate_key_id: attestation_certificate_key_id)
100
101
  else
101
- configuration.acceptable_attestation_types.include?(attestation_type)
102
+ relying_party.acceptable_attestation_types.include?(attestation_type)
102
103
  end
103
104
  end
104
105
 
@@ -122,7 +123,7 @@ module WebAuthn
122
123
 
123
124
  def root_certificates(aaguid: nil, attestation_certificate_key_id: nil)
124
125
  root_certificates =
125
- configuration.attestation_root_certificates_finders.reduce([]) do |certs, finder|
126
+ relying_party.attestation_root_certificates_finders.reduce([]) do |certs, finder|
126
127
  if certs.empty?
127
128
  finder.find(
128
129
  attestation_format: format,
@@ -169,14 +170,10 @@ module WebAuthn
169
170
  def cose_algorithm
170
171
  @cose_algorithm ||=
171
172
  COSE::Algorithm.find(algorithm).tap do |alg|
172
- alg && configuration.algorithms.include?(alg.name) ||
173
+ alg && relying_party.algorithms.include?(alg.name) ||
173
174
  raise(UnsupportedAlgorithm, "Unsupported algorithm #{algorithm}")
174
175
  end
175
176
  end
176
-
177
- def configuration
178
- WebAuthn.configuration
179
- end
180
177
  end
181
178
  end
182
179
  end
@@ -10,8 +10,8 @@ module WebAuthn
10
10
  class SignCountVerificationError < VerificationError; end
11
11
 
12
12
  class AuthenticatorAssertionResponse < AuthenticatorResponse
13
- def self.from_client(response)
14
- encoder = WebAuthn.configuration.encoder
13
+ def self.from_client(response, relying_party: WebAuthn.configuration.relying_party)
14
+ encoder = relying_party.encoder
15
15
 
16
16
  user_handle =
17
17
  if response["userHandle"]
@@ -22,7 +22,8 @@ module WebAuthn
22
22
  authenticator_data: encoder.decode(response["authenticatorData"]),
23
23
  client_data_json: encoder.decode(response["clientDataJSON"]),
24
24
  signature: encoder.decode(response["signature"]),
25
- user_handle: user_handle
25
+ user_handle: user_handle,
26
+ relying_party: relying_party
26
27
  )
27
28
  end
28
29
 
@@ -18,12 +18,13 @@ module WebAuthn
18
18
  class AuthenticatorAttestationResponse < AuthenticatorResponse
19
19
  extend Forwardable
20
20
 
21
- def self.from_client(response)
22
- encoder = WebAuthn.configuration.encoder
21
+ def self.from_client(response, relying_party: WebAuthn.configuration.relying_party)
22
+ encoder = relying_party.encoder
23
23
 
24
24
  new(
25
25
  attestation_object: encoder.decode(response["attestationObject"]),
26
- client_data_json: encoder.decode(response["clientDataJSON"])
26
+ client_data_json: encoder.decode(response["clientDataJSON"]),
27
+ relying_party: relying_party
27
28
  )
28
29
  end
29
30
 
@@ -33,13 +34,14 @@ module WebAuthn
33
34
  super(**options)
34
35
 
35
36
  @attestation_object_bytes = attestation_object
37
+ @relying_party = relying_party
36
38
  end
37
39
 
38
40
  def verify(expected_challenge, expected_origin = nil, user_verification: nil, rp_id: nil)
39
41
  super
40
42
 
41
43
  verify_item(:attested_credential)
42
- if WebAuthn.configuration.verify_attestation_statement
44
+ if relying_party.verify_attestation_statement
43
45
  verify_item(:attestation_statement)
44
46
  end
45
47
 
@@ -47,7 +49,7 @@ module WebAuthn
47
49
  end
48
50
 
49
51
  def attestation_object
50
- @attestation_object ||= WebAuthn::AttestationObject.deserialize(attestation_object_bytes)
52
+ @attestation_object ||= WebAuthn::AttestationObject.deserialize(attestation_object_bytes, relying_party)
51
53
  end
52
54
 
53
55
  def_delegators(
@@ -63,14 +65,15 @@ module WebAuthn
63
65
 
64
66
  private
65
67
 
66
- attr_reader :attestation_object_bytes
68
+ attr_reader :attestation_object_bytes, :relying_party
67
69
 
68
70
  def type
69
71
  WebAuthn::TYPES[:create]
70
72
  end
71
73
 
72
74
  def valid_attested_credential?
73
- attestation_object.valid_attested_credential?
75
+ attestation_object.valid_attested_credential? &&
76
+ relying_party.algorithms.include?(authenticator_data.credential.algorithm)
74
77
  end
75
78
 
76
79
  def valid_attestation_statement?
@@ -24,7 +24,7 @@ module WebAuthn
24
24
 
25
25
  # TODO: use keyword_init when we dropped Ruby 2.4 support
26
26
  Credential =
27
- Struct.new(:id, :public_key) do
27
+ Struct.new(:id, :public_key, :algorithm) do
28
28
  def public_key_object
29
29
  COSE::Key.deserialize(public_key).to_pkey
30
30
  end
@@ -47,7 +47,7 @@ module WebAuthn
47
47
  def credential
48
48
  @credential ||=
49
49
  if valid?
50
- Credential.new(id, public_key)
50
+ Credential.new(id, public_key, algorithm)
51
51
  end
52
52
  end
53
53
 
@@ -59,10 +59,16 @@ module WebAuthn
59
59
 
60
60
  private
61
61
 
62
+ def algorithm
63
+ COSE::Algorithm.find(cose_key.alg).name
64
+ end
65
+
62
66
  def valid_credential_public_key?
63
- cose_key = COSE::Key.deserialize(public_key)
67
+ !!cose_key.alg
68
+ end
64
69
 
65
- !!cose_key.alg && WebAuthn.configuration.algorithms.include?(COSE::Algorithm.find(cose_key.alg).name)
70
+ def cose_key
71
+ @cose_key ||= COSE::Key.deserialize(public_key)
66
72
  end
67
73
 
68
74
  def public_key
@@ -20,13 +20,14 @@ module WebAuthn
20
20
  class UserVerifiedVerificationError < VerificationError; end
21
21
 
22
22
  class AuthenticatorResponse
23
- def initialize(client_data_json:)
23
+ def initialize(client_data_json:, relying_party: WebAuthn.configuration.relying_party)
24
24
  @client_data_json = client_data_json
25
+ @relying_party = relying_party
25
26
  end
26
27
 
27
28
  def verify(expected_challenge, expected_origin = nil, user_verification: nil, rp_id: nil)
28
- expected_origin ||= WebAuthn.configuration.origin || raise("Unspecified expected origin")
29
- rp_id ||= WebAuthn.configuration.rp_id
29
+ expected_origin ||= relying_party.origin || raise("Unspecified expected origin")
30
+ rp_id ||= relying_party.id
30
31
 
31
32
  verify_item(:type)
32
33
  verify_item(:token_binding)
@@ -35,7 +36,7 @@ module WebAuthn
35
36
  verify_item(:authenticator_data)
36
37
  verify_item(:rp_id, rp_id || rp_id_from_origin(expected_origin))
37
38
 
38
- if !WebAuthn.configuration.silent_authentication
39
+ if !relying_party.silent_authentication
39
40
  verify_item(:user_presence)
40
41
  end
41
42
 
@@ -58,7 +59,7 @@ module WebAuthn
58
59
 
59
60
  private
60
61
 
61
- attr_reader :client_data_json
62
+ attr_reader :client_data_json, :relying_party
62
63
 
63
64
  def verify_item(item, *args)
64
65
  if send("valid_#{item}?", *args)
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "openssl"
4
- require "webauthn/encoder"
5
- require "webauthn/error"
3
+ require 'forwardable'
4
+ require 'webauthn/relying_party'
6
5
 
7
6
  module WebAuthn
8
7
  def self.configuration
@@ -13,54 +12,49 @@ module WebAuthn
13
12
  yield(configuration)
14
13
  end
15
14
 
16
- class RootCertificateFinderNotSupportedError < Error; end
17
-
18
15
  class Configuration
19
- def self.if_pss_supported(algorithm)
20
- OpenSSL::PKey::RSA.instance_methods.include?(:verify_pss) ? algorithm : nil
21
- end
22
-
23
- DEFAULT_ALGORITHMS = ["ES256", if_pss_supported("PS256"), "RS256"].compact.freeze
24
-
25
- attr_accessor :algorithms
26
- attr_accessor :encoding
27
- attr_accessor :origin
28
- attr_accessor :rp_id
29
- attr_accessor :rp_name
30
- attr_accessor :verify_attestation_statement
31
- attr_accessor :credential_options_timeout
32
- attr_accessor :silent_authentication
33
- attr_accessor :acceptable_attestation_types
34
- attr_reader :attestation_root_certificates_finders
16
+ extend Forwardable
17
+
18
+ def_delegators :@relying_party,
19
+ :algorithms,
20
+ :algorithms=,
21
+ :encoding,
22
+ :encoding=,
23
+ :origin,
24
+ :origin=,
25
+ :verify_attestation_statement,
26
+ :verify_attestation_statement=,
27
+ :credential_options_timeout,
28
+ :credential_options_timeout=,
29
+ :silent_authentication,
30
+ :silent_authentication=,
31
+ :acceptable_attestation_types,
32
+ :acceptable_attestation_types=,
33
+ :attestation_root_certificates_finders,
34
+ :attestation_root_certificates_finders=,
35
+ :encoder,
36
+ :encoder=
37
+
38
+ attr_reader :relying_party
35
39
 
36
40
  def initialize
37
- @algorithms = DEFAULT_ALGORITHMS.dup
38
- @encoding = WebAuthn::Encoder::STANDARD_ENCODING
39
- @verify_attestation_statement = true
40
- @credential_options_timeout = 120000
41
- @silent_authentication = false
42
- @acceptable_attestation_types = ['None', 'Self', 'Basic', 'AttCA', 'Basic_or_AttCA']
43
- @attestation_root_certificates_finders = []
41
+ @relying_party = RelyingParty.new
44
42
  end
45
43
 
46
- # This is the user-data encoder.
47
- # Used to decode user input and to encode data provided to the user.
48
- def encoder
49
- @encoder ||= WebAuthn::Encoder.new(encoding)
44
+ def rp_name
45
+ relying_party.name
50
46
  end
51
47
 
52
- def attestation_root_certificates_finders=(finders)
53
- if !finders.respond_to?(:each)
54
- finders = [finders]
55
- end
48
+ def rp_name=(name)
49
+ relying_party.name = name
50
+ end
56
51
 
57
- finders.each do |finder|
58
- unless finder.respond_to?(:find)
59
- raise RootCertificateFinderNotSupportedError, "Finder must implement `find` method"
60
- end
61
- end
52
+ def rp_id
53
+ relying_party.id
54
+ end
62
55
 
63
- @attestation_root_certificates_finders = finders
56
+ def rp_id=(id)
57
+ relying_party.id = id
64
58
  end
65
59
  end
66
60
  end
@@ -4,6 +4,7 @@ require "webauthn/public_key_credential/creation_options"
4
4
  require "webauthn/public_key_credential/request_options"
5
5
  require "webauthn/public_key_credential_with_assertion"
6
6
  require "webauthn/public_key_credential_with_attestation"
7
+ require "webauthn/relying_party"
7
8
 
8
9
  module WebAuthn
9
10
  module Credential
@@ -15,12 +16,12 @@ module WebAuthn
15
16
  WebAuthn::PublicKeyCredential::RequestOptions.new(**keyword_arguments)
16
17
  end
17
18
 
18
- def self.from_create(credential)
19
- WebAuthn::PublicKeyCredentialWithAttestation.from_client(credential)
19
+ def self.from_create(credential, relying_party: WebAuthn.configuration.relying_party)
20
+ WebAuthn::PublicKeyCredentialWithAttestation.from_client(credential, relying_party: relying_party)
20
21
  end
21
22
 
22
- def self.from_get(credential)
23
- WebAuthn::PublicKeyCredentialWithAssertion.from_client(credential)
23
+ def self.from_get(credential, relying_party: WebAuthn.configuration.relying_party)
24
+ WebAuthn::PublicKeyCredentialWithAssertion.from_client(credential, relying_party: relying_party)
24
25
  end
25
26
  end
26
27
  end
@@ -10,7 +10,7 @@ module WebAuthn
10
10
  class FakeClient
11
11
  TYPES = { create: "webauthn.create", get: "webauthn.get" }.freeze
12
12
 
13
- attr_reader :origin, :token_binding
13
+ attr_reader :origin, :token_binding, :encoding
14
14
 
15
15
  def initialize(
16
16
  origin = fake_origin,
@@ -104,7 +104,7 @@ module WebAuthn
104
104
 
105
105
  private
106
106
 
107
- attr_reader :authenticator, :encoding
107
+ attr_reader :authenticator
108
108
 
109
109
  def data_json_for(method, challenge)
110
110
  data = {
@@ -6,22 +6,31 @@ module WebAuthn
6
6
  class PublicKeyCredential
7
7
  attr_reader :type, :id, :raw_id, :client_extension_outputs, :response
8
8
 
9
- def self.from_client(credential)
9
+ def self.from_client(credential, relying_party: WebAuthn.configuration.relying_party)
10
10
  new(
11
11
  type: credential["type"],
12
12
  id: credential["id"],
13
- raw_id: WebAuthn.configuration.encoder.decode(credential["rawId"]),
13
+ raw_id: relying_party.encoder.decode(credential["rawId"]),
14
14
  client_extension_outputs: credential["clientExtensionResults"],
15
- response: response_class.from_client(credential["response"])
15
+ response: response_class.from_client(credential["response"], relying_party: relying_party),
16
+ encoder: relying_party.encoder
16
17
  )
17
18
  end
18
19
 
19
- def initialize(type:, id:, raw_id:, client_extension_outputs: {}, response:)
20
+ def initialize(
21
+ type:,
22
+ id:,
23
+ raw_id:,
24
+ response:,
25
+ client_extension_outputs: {},
26
+ encoder: WebAuthn.configuration.encoder
27
+ )
20
28
  @type = type
21
29
  @id = id
22
30
  @raw_id = raw_id
23
31
  @client_extension_outputs = client_extension_outputs
24
32
  @response = response
33
+ @encoder = encoder
25
34
  end
26
35
 
27
36
  def verify(*_args)
@@ -41,6 +50,8 @@ module WebAuthn
41
50
 
42
51
  private
43
52
 
53
+ attr_reader :encoder
54
+
44
55
  def valid_type?
45
56
  type == TYPE_PUBLIC_KEY
46
57
  end
@@ -52,9 +63,5 @@ module WebAuthn
52
63
  def authenticator_data
53
64
  response&.authenticator_data
54
65
  end
55
-
56
- def encoder
57
- WebAuthn.configuration.encoder
58
- end
59
66
  end
60
67
  end
@@ -39,8 +39,8 @@ module WebAuthn
39
39
 
40
40
  @rp =
41
41
  if rp.is_a?(Hash)
42
- rp[:name] ||= configuration.rp_name
43
- rp[:id] ||= configuration.rp_id
42
+ rp[:name] ||= relying_party.name
43
+ rp[:id] ||= relying_party.id
44
44
 
45
45
  RPEntity.new(**rp)
46
46
  else
@@ -76,7 +76,7 @@ module WebAuthn
76
76
  end
77
77
 
78
78
  def pub_key_cred_params_from_algs
79
- Array(algs || configuration.algorithms).map do |alg|
79
+ Array(algs || relying_party.algorithms).map do |alg|
80
80
  alg_id =
81
81
  if alg.is_a?(String) || alg.is_a?(Symbol)
82
82
  COSE::Algorithm.by_name(alg.to_s).id
@@ -8,10 +8,11 @@ module WebAuthn
8
8
  class Options
9
9
  CHALLENGE_LENGTH = 32
10
10
 
11
- attr_reader :timeout, :extensions
11
+ attr_reader :timeout, :extensions, :relying_party
12
12
 
13
- def initialize(timeout: default_timeout, extensions: nil)
14
- @timeout = timeout
13
+ def initialize(timeout: nil, extensions: nil, relying_party: WebAuthn.configuration.relying_party)
14
+ @relying_party = relying_party
15
+ @timeout = timeout || default_timeout
15
16
  @extensions = extensions
16
17
  end
17
18
 
@@ -49,7 +50,7 @@ module WebAuthn
49
50
  end
50
51
 
51
52
  def encoder
52
- WebAuthn.configuration.encoder
53
+ relying_party.encoder
53
54
  end
54
55
 
55
56
  def raw_challenge
@@ -57,11 +58,7 @@ module WebAuthn
57
58
  end
58
59
 
59
60
  def default_timeout
60
- configuration.credential_options_timeout
61
- end
62
-
63
- def configuration
64
- WebAuthn.configuration
61
+ relying_party.credential_options_timeout
65
62
  end
66
63
 
67
64
  def as_public_key_descriptors(ids)
@@ -10,7 +10,7 @@ module WebAuthn
10
10
  def initialize(rp_id: nil, allow_credentials: nil, allow: nil, user_verification: nil, **keyword_arguments)
11
11
  super(**keyword_arguments)
12
12
 
13
- @rp_id = rp_id || configuration.rp_id
13
+ @rp_id = rp_id || relying_party.id
14
14
  @allow_credentials = allow_credentials
15
15
  @allow = allow
16
16
  @user_verification = user_verification
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+ require "webauthn/credential"
5
+ require "webauthn/encoder"
6
+ require "webauthn/error"
7
+
8
+ module WebAuthn
9
+ class RootCertificateFinderNotSupportedError < Error; end
10
+
11
+ class RelyingParty
12
+ def self.if_pss_supported(algorithm)
13
+ OpenSSL::PKey::RSA.instance_methods.include?(:verify_pss) ? algorithm : nil
14
+ end
15
+
16
+ DEFAULT_ALGORITHMS = ["ES256", if_pss_supported("PS256"), "RS256"].compact.freeze
17
+
18
+ def initialize(
19
+ algorithms: DEFAULT_ALGORITHMS.dup,
20
+ encoding: WebAuthn::Encoder::STANDARD_ENCODING,
21
+ origin: nil,
22
+ id: nil,
23
+ name: nil,
24
+ verify_attestation_statement: true,
25
+ credential_options_timeout: 120000,
26
+ silent_authentication: false,
27
+ acceptable_attestation_types: ['None', 'Self', 'Basic', 'AttCA', 'Basic_or_AttCA'],
28
+ attestation_root_certificates_finders: []
29
+ )
30
+ @algorithms = algorithms
31
+ @encoding = encoding
32
+ @origin = origin
33
+ @id = id
34
+ @name = name
35
+ @verify_attestation_statement = verify_attestation_statement
36
+ @credential_options_timeout = credential_options_timeout
37
+ @silent_authentication = silent_authentication
38
+ @acceptable_attestation_types = acceptable_attestation_types
39
+ self.attestation_root_certificates_finders = attestation_root_certificates_finders
40
+ end
41
+
42
+ attr_accessor :algorithms,
43
+ :encoding,
44
+ :origin,
45
+ :id,
46
+ :name,
47
+ :verify_attestation_statement,
48
+ :credential_options_timeout,
49
+ :silent_authentication,
50
+ :acceptable_attestation_types
51
+
52
+ attr_reader :attestation_root_certificates_finders
53
+
54
+ # This is the user-data encoder.
55
+ # Used to decode user input and to encode data provided to the user.
56
+ def encoder
57
+ @encoder ||= WebAuthn::Encoder.new(encoding)
58
+ end
59
+
60
+ def attestation_root_certificates_finders=(finders)
61
+ if !finders.respond_to?(:each)
62
+ finders = [finders]
63
+ end
64
+
65
+ finders.each do |finder|
66
+ unless finder.respond_to?(:find)
67
+ raise RootCertificateFinderNotSupportedError, "Finder must implement `find` method"
68
+ end
69
+ end
70
+
71
+ @attestation_root_certificates_finders = finders
72
+ end
73
+
74
+ def options_for_registration(**keyword_arguments)
75
+ WebAuthn::Credential.options_for_create(
76
+ **keyword_arguments,
77
+ relying_party: self
78
+ )
79
+ end
80
+
81
+ def verify_registration(raw_credential, challenge, user_verification: nil)
82
+ webauthn_credential = WebAuthn::Credential.from_create(raw_credential, relying_party: self)
83
+
84
+ if webauthn_credential.verify(challenge, user_verification: user_verification)
85
+ webauthn_credential
86
+ end
87
+ end
88
+
89
+ def options_for_authentication(**keyword_arguments)
90
+ WebAuthn::Credential.options_for_get(
91
+ **keyword_arguments,
92
+ relying_party: self
93
+ )
94
+ end
95
+
96
+ def verify_authentication(
97
+ raw_credential,
98
+ challenge,
99
+ user_verification: nil,
100
+ public_key: nil,
101
+ sign_count: nil
102
+ )
103
+ webauthn_credential = WebAuthn::Credential.from_get(raw_credential, relying_party: self)
104
+
105
+ stored_credential = yield(webauthn_credential) if block_given?
106
+
107
+ if webauthn_credential.verify(
108
+ challenge,
109
+ public_key: public_key || stored_credential.public_key,
110
+ sign_count: sign_count || stored_credential.sign_count,
111
+ user_verification: user_verification
112
+ )
113
+ block_given? ? [webauthn_credential, stored_credential] : webauthn_credential
114
+ end
115
+ end
116
+ end
117
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WebAuthn
4
- VERSION = "2.3.0"
4
+ VERSION = "3.0.0.alpha1"
5
5
  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: 2.3.0
4
+ version: 3.0.0.alpha1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gonzalo Rodriguez
@@ -313,6 +313,7 @@ files:
313
313
  - lib/webauthn/public_key_credential/user_entity.rb
314
314
  - lib/webauthn/public_key_credential_with_assertion.rb
315
315
  - lib/webauthn/public_key_credential_with_attestation.rb
316
+ - lib/webauthn/relying_party.rb
316
317
  - lib/webauthn/security_utils.rb
317
318
  - lib/webauthn/u2f_migrator.rb
318
319
  - lib/webauthn/version.rb
@@ -337,9 +338,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
337
338
  version: '2.4'
338
339
  required_rubygems_version: !ruby/object:Gem::Requirement
339
340
  requirements:
340
- - - ">="
341
+ - - ">"
341
342
  - !ruby/object:Gem::Version
342
- version: '0'
343
+ version: 1.3.1
343
344
  requirements: []
344
345
  rubygems_version: 3.1.4
345
346
  signing_key: