webauthn 3.0.0 → 3.4.2

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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.github/actions/install-openssl/action.yml +55 -0
  3. data/.github/actions/install-ruby/action.yml +84 -0
  4. data/.github/dependabot.yml +6 -0
  5. data/.github/workflows/build.yml +60 -10
  6. data/.github/workflows/git.yml +1 -1
  7. data/.rubocop.yml +1 -1
  8. data/CHANGELOG.md +77 -0
  9. data/README.md +5 -4
  10. data/docs/advanced_configuration.md +16 -15
  11. data/lib/webauthn/attestation_statement/base.rb +10 -1
  12. data/lib/webauthn/authenticator_assertion_response.rb +16 -2
  13. data/lib/webauthn/authenticator_attestation_response.rb +5 -4
  14. data/lib/webauthn/authenticator_response.rb +16 -6
  15. data/lib/webauthn/client_data.rb +4 -6
  16. data/lib/webauthn/configuration.rb +2 -0
  17. data/lib/webauthn/credential.rb +0 -1
  18. data/lib/webauthn/encoder.rb +5 -33
  19. data/lib/webauthn/encoders.rb +62 -0
  20. data/lib/webauthn/fake_authenticator/attestation_object.rb +10 -1
  21. data/lib/webauthn/fake_authenticator/authenticator_data.rb +3 -1
  22. data/lib/webauthn/fake_authenticator.rb +25 -4
  23. data/lib/webauthn/fake_client.rb +6 -1
  24. data/lib/webauthn/json_serializer.rb +45 -0
  25. data/lib/webauthn/public_key_credential/entity.rb +2 -24
  26. data/lib/webauthn/public_key_credential/options.rb +2 -24
  27. data/lib/webauthn/public_key_credential.rb +17 -2
  28. data/lib/webauthn/public_key_credential_with_assertion.rb +2 -1
  29. data/lib/webauthn/public_key_credential_with_attestation.rb +2 -2
  30. data/lib/webauthn/relying_party.rb +24 -7
  31. data/lib/webauthn/u2f_migrator.rb +5 -3
  32. data/lib/webauthn/version.rb +1 -1
  33. data/lib/webauthn.rb +1 -0
  34. data/webauthn.gemspec +5 -6
  35. metadata +26 -29
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b133f46bd003c9bfb9a8557bb8caeca7239fcd6c185f78e74e0ae578444d576e
4
- data.tar.gz: 14a32debc72ddcfe7e752118ebd4db832b8a46a5628ee54830f30eada4159bcf
3
+ metadata.gz: 50b5c2c43f3e5719dd1ed0f63e9a7bb9705f2f1e97861ab0a0ee0a7d61c4ee41
4
+ data.tar.gz: d8849b387d30e54f7a45fd66bcb2b164983100b0b3369ca8fbf52840261e7fe4
5
5
  SHA512:
6
- metadata.gz: 9b4ff5cee18575b517473fef7d8000d7e41f113b178378e64a4d2b9036d248f4295edd846a519ab76036a270c3066020fbe44847019812c2a5758f2b1b428f4c
7
- data.tar.gz: f3ab22709cbf537982e163acc60dbe114217f223b18cb4cf681a3d932e2f31276f8286091e49da392356c1958ada7e2c07a5b86b48d57b597f965a57c1bcf63d
6
+ metadata.gz: 4189cd8d89340585a464e6d9368b6ea532c7c59189cbd47022a04d767ec230979076652e333e70878dcead127688d7fe01e57706f63afe325eb1ec5673cf851a
7
+ data.tar.gz: 1cba3b28cd9388256f6d47493fb151f7732ad8680da5863476d4b5e237db7389e362f7a2543bc1447cd0e205e44134957cfa2e4ceb43e9fb32a6b5f851ff349a
@@ -0,0 +1,55 @@
1
+ name: Install OpenSSL
2
+
3
+ inputs:
4
+ version:
5
+ description: 'The version of OpenSSL to install'
6
+ required: true
7
+
8
+ runs:
9
+ using: 'composite'
10
+ steps:
11
+ - name: Restore cached OpenSSL library
12
+ id: cache-openssl-restore
13
+ uses: actions/cache/restore@v4
14
+ with:
15
+ path: ~/openssl
16
+ key: openssl-${{ inputs.version }}
17
+
18
+ - name: Compile OpenSSL library
19
+ if: steps.cache-openssl-restore.outputs.cache-hit != 'true'
20
+ shell: bash
21
+ run: |
22
+ mkdir -p tmp/build-openssl && cd tmp/build-openssl
23
+ case ${{ inputs.version }} in
24
+ 1.1.*)
25
+ OPENSSL_COMMIT=OpenSSL_
26
+ OPENSSL_COMMIT+=$(echo ${{ inputs.version }} | sed -e 's/\./_/g')
27
+ git clone -b $OPENSSL_COMMIT --depth 1 https://github.com/openssl/openssl.git .
28
+ echo "Git commit: $(git rev-parse HEAD)"
29
+ ./Configure --prefix=$HOME/openssl --libdir=lib linux-x86_64
30
+ make depend && make -j4 && make install_sw
31
+ ;;
32
+ 3.*)
33
+ OPENSSL_COMMIT=openssl-
34
+ OPENSSL_COMMIT+=$(echo ${{ inputs.version }})
35
+ git clone -b $OPENSSL_COMMIT --depth 1 https://github.com/openssl/openssl.git .
36
+ echo "Git commit: $(git rev-parse HEAD)"
37
+ if [[ ${{ inputs.version }} == 3.5* ]]; then
38
+ ./Configure --prefix=$HOME/openssl --libdir=lib enable-fips no-tests no-legacy
39
+ else
40
+ ./Configure --prefix=$HOME/openssl --libdir=lib enable-fips no-tests
41
+ fi
42
+ make -j4 && make install_sw && make install_fips
43
+ ;;
44
+ *)
45
+ echo "Don't know how to build OpenSSL ${{ inputs.version }}"
46
+ ;;
47
+ esac
48
+
49
+ - name: Save OpenSSL library cache
50
+ if: steps.cache-openssl-restore.outputs.cache-hit != 'true'
51
+ id: cache-openssl-save
52
+ uses: actions/cache/save@v4
53
+ with:
54
+ path: ~/openssl
55
+ key: ${{ steps.cache-openssl-restore.outputs.cache-primary-key }}
@@ -0,0 +1,84 @@
1
+ name: Install Ruby
2
+
3
+ inputs:
4
+ version:
5
+ description: 'The version of Ruby to install'
6
+ required: true
7
+ openssl-version:
8
+ description: 'The version of OpenSSL used'
9
+ required: true
10
+
11
+ runs:
12
+ using: 'composite'
13
+ steps:
14
+ - name: Restore cached Ruby installation
15
+ id: cache-ruby-restore
16
+ uses: actions/cache/restore@v4
17
+ with:
18
+ path: ~/rubies/ruby-${{ inputs.version }}
19
+ key: ruby-${{ inputs.version }}-with-openssl-${{ inputs.openssl-version }}
20
+
21
+ - name: Install Ruby
22
+ if: steps.cache-ruby-restore.outputs.cache-hit != 'true'
23
+ shell: bash
24
+ run: |
25
+ latest_patch=$(curl -s https://cache.ruby-lang.org/pub/ruby/${{ inputs.version }}/ \
26
+ | grep -oP "ruby-${{ inputs.version }}\.\d+\.tar\.xz" \
27
+ | grep -oP "\d+(?=\.tar\.xz)" \
28
+ | sort -V | tail -n 1)
29
+ wget https://cache.ruby-lang.org/pub/ruby/${{ inputs.version }}/ruby-${{ inputs.version }}.${latest_patch}.tar.xz
30
+ tar -xJvf ruby-${{ inputs.version }}.${latest_patch}.tar.xz
31
+ cd ruby-${{ inputs.version }}.${latest_patch}
32
+ ./configure --prefix=$HOME/rubies/ruby-${{ inputs.version }} --with-openssl-dir=$HOME/openssl
33
+ make
34
+ make install
35
+
36
+ - name: Update PATH
37
+ shell: bash
38
+ run: |
39
+ echo "~/rubies/ruby-${{ inputs.version }}/bin" >> $GITHUB_PATH
40
+
41
+ - name: Install Bundler
42
+ shell: bash
43
+ run: |
44
+ case ${{ inputs.version }} in
45
+ 2.7* | 3.*)
46
+ echo "Skipping Bundler installation for Ruby ${{ inputs.version }}"
47
+ ;;
48
+ 2.5* | 2.6*)
49
+ gem install bundler -v '~> 2.3.0'
50
+ ;;
51
+ *)
52
+ echo "Don't know how to install Bundler for Ruby ${{ inputs.version }}"
53
+ ;;
54
+ esac
55
+
56
+ - name: Save Ruby installation cache
57
+ if: steps.cache-ruby-restore.outputs.cache-hit != 'true'
58
+ id: cache-ruby-save
59
+ uses: actions/cache/save@v4
60
+ with:
61
+ path: ~/rubies/ruby-${{ inputs.version }}
62
+ key: ${{ steps.cache-ruby-restore.outputs.cache-primary-key }}
63
+
64
+ - name: Cache Bundler Install
65
+ id: cache-bundler-restore
66
+ uses: actions/cache/restore@v4
67
+ env:
68
+ GEMFILE: ${{ env.BUNDLE_GEMFILE || 'Gemfile' }}
69
+ with:
70
+ path: ~/bundler/cache
71
+ key: bundler-ruby-${{ inputs.version }}-${{ inputs.openssl-version }}-${{ hashFiles(env.Gemfile, 'webauthn.gemspec') }}
72
+
73
+ - name: Install dependencies
74
+ shell: bash
75
+ run: |
76
+ bundle config set --local path ~/bundler/cache
77
+ bundle install
78
+
79
+ - name: Save Bundler Install cache
80
+ id: cache-bundler-save
81
+ uses: actions/cache/save@v4
82
+ with:
83
+ path: ~/bundler/cache
84
+ key: ${{ steps.cache-bundler-restore.outputs.cache-primary-key }}
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
@@ -7,26 +7,76 @@
7
7
 
8
8
  name: build
9
9
 
10
- on: push
10
+ on:
11
+ push:
12
+ branches: [master]
13
+ pull_request:
14
+ types: [opened, synchronize]
11
15
 
12
16
  jobs:
13
17
  test:
14
- runs-on: ubuntu-20.04
18
+ name: 'Test Ruby ${{ matrix.ruby }} with OpenSSL ${{ matrix.openssl }}'
19
+ runs-on: ubuntu-24.04
15
20
  strategy:
16
21
  fail-fast: false
17
22
  matrix:
18
23
  ruby:
24
+ - '3.4'
25
+ - '3.3'
19
26
  - '3.2'
20
27
  - '3.1'
21
- - '3.0'
22
- - '2.7'
23
- - '2.6'
24
- - '2.5'
25
- - truffleruby
28
+ openssl:
29
+ - '3.5.3'
30
+ - '3.4.2'
31
+ - '3.3.4'
32
+ - '3.2.5'
33
+ - '3.1.8'
34
+ - '3.0.17'
35
+ - '1.1.1w'
36
+ include:
37
+ - ruby: truffleruby
38
+ - ruby: '3.0'
39
+ openssl: '1.1.1w'
40
+ - ruby: '2.7'
41
+ openssl: '1.1.1w'
42
+ - ruby: '2.6'
43
+ openssl: '1.1.1w'
44
+ - ruby: '2.5'
45
+ openssl: '1.1.1w'
46
+
26
47
  steps:
27
- - uses: actions/checkout@v3
28
- - uses: ruby/setup-ruby@v1
48
+ - uses: actions/checkout@v5
49
+
50
+ - name: Install OpenSSL
51
+ if: matrix.ruby != 'truffleruby'
52
+ uses: ./.github/actions/install-openssl
53
+ with:
54
+ version: ${{ matrix.openssl }}
55
+
56
+ - name: Manually set up Ruby
57
+ if: matrix.ruby != 'truffleruby'
58
+ uses: ./.github/actions/install-ruby
59
+ with:
60
+ version: ${{ matrix.ruby }}
61
+ openssl-version: ${{ matrix.openssl }}
62
+
63
+ - name: Set up Ruby
64
+ if: matrix.ruby == 'truffleruby'
65
+ uses: ruby/setup-ruby@v1
29
66
  with:
30
67
  ruby-version: ${{ matrix.ruby }}
31
68
  bundler-cache: true
32
- - run: bundle exec rake
69
+
70
+ - run: bundle exec rspec
71
+ env:
72
+ RUBYOPT: ${{ startsWith(matrix.ruby, '3.4') && '--enable=frozen-string-literal' || '' }}
73
+
74
+ lint:
75
+ runs-on: ubuntu-latest
76
+ steps:
77
+ - uses: actions/checkout@v5
78
+ - uses: ruby/setup-ruby@v1
79
+ with:
80
+ ruby-version: '3.3'
81
+ bundler-cache: true
82
+ - run: bundle exec rubocop -f github
@@ -14,7 +14,7 @@ jobs:
14
14
  runs-on: ubuntu-latest
15
15
 
16
16
  steps:
17
- - uses: actions/checkout@v3
17
+ - uses: actions/checkout@v5
18
18
  - name: Block autosquash commits
19
19
  uses: xt0rted/block-autosquash-commits-action@v2
20
20
  with:
data/.rubocop.yml CHANGED
@@ -1,4 +1,4 @@
1
- require:
1
+ plugins:
2
2
  - rubocop-rspec
3
3
  - rubocop-rake
4
4
 
data/CHANGELOG.md CHANGED
@@ -1,5 +1,74 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.4.2] - 2025-09-22
4
+
5
+ ### Added
6
+
7
+ - Updated `safety_net_attestation` dependency from `~> 0.4.0` to `~> 0.5.0`.
8
+
9
+ ## [v3.4.1] - 2025-06-06
10
+
11
+ - Avoid requiring `base64` as it's not a direct dependency. [#459](https://github.com/cedarcode/webauthn-ruby/pull/459)[@santiagorodriguez96]
12
+
13
+ ## [v3.4.0] - 2025-02-17
14
+
15
+ - Added support for Webauthn.config and RelayingParty to accept multiple allowed_origins. [#431](https://github.com/cedarcode/webauthn-ruby/pull/431)[@obroshnij]
16
+
17
+ ## [v3.3.0] - 2025-02-06
18
+
19
+ ### Added
20
+
21
+ - Updated `tpm-key_attestation` dependency from `~> 0.12.0` to `~> 0.14.0`. [#449](https://github.com/cedarcode/webauthn-ruby/pull/449) [@brauliomartinezlm], [@nicolastemciuc]
22
+
23
+ ## [v3.2.2] - 2024-11-14
24
+
25
+ ### Fixed
26
+
27
+ - Fix `PublicKeyCredential::Options#.as_json` not camelCase'ing keys of attributes with hash or arrays as values. [#445](https://github.com/cedarcode/webauthn-ruby/pull/445) [@santiagorodriguez96]
28
+
29
+ ## [v3.2.1] - 2024-11-14
30
+
31
+ ### Fixed
32
+
33
+ - Fix JSON Serializer generating json with attributes with a null value. [#442](https://github.com/cedarcode/webauthn-ruby/pull/442) @santiagorodriguez96
34
+
35
+ ## [v3.2.0] - 2024-11-13
36
+
37
+ ### Added
38
+
39
+ - Added `AuthenticatorAttestationResponse#transports` for accessing the response's `transports` value. [#421](https://github.com/cedarcode/webauthn-ruby/pull/421) [@santiagorodriguez96]
40
+ - `WebAuthn::AuthenticatorAssertionResponse#verify` and `WebAuthn::AuthenticatorAttestationResponse#verify`,
41
+ as well as `RelyingParty#verify_registration` and `RelyingParty#verify_authentication` now accept a `user_presence`
42
+ keyword arg in order to be able to skip the user presence check for specific attestation and assertion verifications.
43
+ By default, user presence will be checked unless `silent_authentication` is enabled for the Relying Party (as it was before).
44
+ [#432](https://github.com/cedarcode/webauthn-ruby/pull/432), [#434](https://github.com/cedarcode/webauthn-ruby/pull/434), [#435](https://github.com/cedarcode/webauthn-ruby/pull/435) ([@nov](https://github.com/nov), [@santiagorodriguez96])
45
+ - `WebAuthn::FakeClient#create` and `WebAuthn::FakeAuthenticator#make_credential` now support a `credential_algorithm` and
46
+ `algorithm` param (respectively) for choosing the algorithm to use for creating the credential.
47
+ Supported values are: 'ES256', 'RSA256' and 'EdDSA'. [#400](https://github.com/cedarcode/webauthn-ruby/pull/400), [#437](https://github.com/cedarcode/webauthn-ruby/pull/437) [@santiagorodriguez96]
48
+ - Remove `awrence` dependency. [#436](https://github.com/cedarcode/webauthn-ruby/pull/436) [@npezza](https://github.com/npezza93)
49
+ - Run tests with Ruby 3.3. [#416](https://github.com/cedarcode/webauthn-ruby/pull/416) [@santiagorodriguez96]
50
+ - Run tests with Ruby 3.4.0-preview2. [#436](https://github.com/cedarcode/webauthn-ruby/pull/436) [@npezza](https://github.com/npezza93)
51
+
52
+ ### Changed
53
+
54
+ - Remove unused class `AttestationTrustworthinessVerificationError`. [#412](https://github.com/cedarcode/webauthn-ruby/pull/412) [@soartec-lab]
55
+
56
+ ## [v3.1.0] - 2023-12-26
57
+
58
+ ### Added
59
+
60
+ - Add support for optional `authenticator_attachment` in `PublicKeyCredential`. #370 [@8ma10s]
61
+
62
+ ### Fixed
63
+
64
+ - Fix circular require warning between `webauthn/relying_party` and `webauthn/credential`. #389 [@bdewater]
65
+ - Correctly verify attestation that contains just a batch certificate that is present in the attestation root certificates. #406 [@santiagorodriguez96]
66
+
67
+ ### Changed
68
+
69
+ - Inlined `base64` implementation. #402 [@olleolleolle]
70
+ - Raise a more descriptive error if input `challenge` is `nil` when verifying the `PublicKeyCredential`. #413 [@soartec-lab]
71
+
3
72
  ## [v3.0.0] - 2023-02-15
4
73
 
5
74
  ### Added
@@ -358,6 +427,14 @@ Note: Both additions should help making it compatible with Chrome for Android 70
358
427
  - `WebAuthn::AuthenticatorAttestationResponse.valid?` can be used to validate fido-u2f attestations returned by the browser
359
428
  - Works with ruby 2.5
360
429
 
430
+ [v3.4.2]: https://github.com/cedarcode/webauthn-ruby/compare/v3.4.1...v3.4.2/
431
+ [v3.4.1]: https://github.com/cedarcode/webauthn-ruby/compare/v3.4.0...v3.4.1/
432
+ [v3.4.0]: https://github.com/cedarcode/webauthn-ruby/compare/v3.3.0...v3.4.0/
433
+ [v3.3.0]: https://github.com/cedarcode/webauthn-ruby/compare/v3.2.2...v3.3.0/
434
+ [v3.2.2]: https://github.com/cedarcode/webauthn-ruby/compare/v3.2.1...v3.2.2/
435
+ [v3.2.1]: https://github.com/cedarcode/webauthn-ruby/compare/v3.2.0...v3.2.1/
436
+ [v3.2.0]: https://github.com/cedarcode/webauthn-ruby/compare/v3.1.0...v3.2.0/
437
+ [v3.1.0]: https://github.com/cedarcode/webauthn-ruby/compare/v3.0.0...v3.1.0/
361
438
  [v3.0.0]: https://github.com/cedarcode/webauthn-ruby/compare/2-stable...v3.0.0/
362
439
  [v3.0.0.alpha2]: https://github.com/cedarcode/webauthn-ruby/compare/2-stable...v3.0.0.alpha2/
363
440
  [v3.0.0.alpha1]: https://github.com/cedarcode/webauthn-ruby/compare/v2.3.0...v3.0.0.alpha1
data/README.md CHANGED
@@ -1,12 +1,12 @@
1
- __Note__: You are viewing the README for the development version of webauthn-ruby.
2
- For the current release version see https://github.com/cedarcode/webauthn-ruby/blob/2-stable/README.md.
1
+ > [!warning]
2
+ > You are viewing the README for the development version of webauthn-ruby. For the current release version see https://github.com/cedarcode/webauthn-ruby/blob/3-stable/README.md.
3
3
 
4
4
  # webauthn-ruby
5
5
 
6
6
  ![banner](assets/webauthn-ruby.png)
7
7
 
8
8
  [![Gem](https://img.shields.io/gem/v/webauthn.svg?style=flat-square)](https://rubygems.org/gems/webauthn)
9
- [![Travis](https://img.shields.io/travis/cedarcode/webauthn-ruby/master.svg?style=flat-square)](https://travis-ci.com/cedarcode/webauthn-ruby)
9
+ [![Build](https://github.com/cedarcode/webauthn-ruby/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/cedarcode/webauthn-ruby/actions/workflows/build.yml)
10
10
  [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-informational.svg?style=flat-square)](https://conventionalcommits.org)
11
11
  [![Join the chat at https://gitter.im/cedarcode/webauthn-ruby](https://badges.gitter.im/cedarcode/webauthn-ruby.svg)](https://gitter.im/cedarcode/webauthn-ruby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
12
12
 
@@ -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."
@@ -4,24 +4,25 @@
4
4
 
5
5
  Which approach suits best your needs will depend on the architecture of your application and how do your users need to register and authenticate to it.
6
6
 
7
- If you have a multi-tenant application, or any application segmenation, where your users register and authenticate to each of these tenants or segments individuallly using different hostnames, or with different security needs, you need to go through [Instance Based Configuration](#instance-based-configuration).
7
+ If you have a multi-tenant application, or any application segmentation, where your users register and authenticate to each of these tenants or segments individually using different hostnames, or with different security needs, you need to go through [Instance Based Configuration](#instance-based-configuration).
8
8
 
9
- However, if your application is served for just one hostname, or else if your users authenticate to only one subdmain (e.g. your application serves www.example.com and admin.example.com but all you users authenticate through auth.example.com) you can still rely on one [Global Configuration](../README.md#configuration).
9
+ However, if your application is served for just one hostname, or else if your users authenticate to only one subdomain (e.g. your application serves www.example.com and admin.example.com but all your users authenticate through auth.example.com) you can still rely on one [Global Configuration](../README.md#configuration).
10
10
 
11
11
  If you are still not sure, or want to keep your options open, be aware that [Instance Based Configuration](#instance-based-configuration) is also a valid way of defining a single instance configuration and how you share such configuration across your application, it's up to you.
12
12
 
13
13
 
14
14
  ## Instance Based Configuration
15
15
 
16
- Intead of the [Global Configuration](../README.md#configuration) you place in `config/initializers/webauthn.rb`,
16
+ Instead of the [Global Configuration](../README.md#configuration) you place in `config/initializers/webauthn.rb`,
17
17
  you can now have an on-demand instance of `WebAuthn::RelyingParty` with the same configuration options, that
18
- you can build anywhere in you application, in the following way:
18
+ you can build anywhere in your application, in the following way:
19
19
 
20
20
  ```ruby
21
21
  relying_party = WebAuthn::RelyingParty.new(
22
22
  # This value needs to match `window.location.origin` evaluated by
23
23
  # the User Agent during registration and authentication ceremonies.
24
- origin: "https://admin.example.com",
24
+ # 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.
25
+ allowed_origins: ["https://admin.example.com"],
25
26
 
26
27
  # Relying Party name for display purposes
27
28
  name: "Admin Site for Example Inc."
@@ -57,9 +58,9 @@ Intead of the [Global Configuration](../README.md#configuration) you place in `c
57
58
 
58
59
  ## Instance Based API
59
60
 
60
- **DISCLAIMER: This API was released on version 3.0.0.alpha1 and is still under evaluation. Although it has been throughly tested and it is fully functional it might be changed until the final release of version 3.0.0.**
61
+ **DISCLAIMER: This API was released on version 3.0.0.alpha1 and is still under evaluation. Although it has been thoroughly tested and it is fully functional it might be changed until the final release of version 3.0.0.**
61
62
 
62
- The explanation for each ceremony can be found in depth in [Credential Registration](../README.md#credential-registration) and [Credential Authentication](../README.md#credential-authentication) but if you choose this instance based approach to define your WebAuthn configurations and assuming `relying_party` is the result of an instance you get through `WebAuthn::RelytingParty.new(...)` the code in those explanations needs to be updated to:
63
+ The explanation for each ceremony can be found in depth in [Credential Registration](../README.md#credential-registration) and [Credential Authentication](../README.md#credential-authentication) but if you choose this instance based approach to define your WebAuthn configurations and assuming `relying_party` is the result of an instance you get through `WebAuthn::RelyingParty.new(...)` the code in those explanations needs to be updated to:
63
64
 
64
65
  ### Credential Registration
65
66
 
@@ -73,7 +74,7 @@ end
73
74
 
74
75
  options = relying_party.options_for_registration(
75
76
  user: { id: user.webauthn_id, name: user.name },
76
- exclude: user.credentials.map { |c| c.webauthn_id }
77
+ exclude: user.credentials.map { |c| c.external_id }
77
78
  )
78
79
 
79
80
  # Store the newly generated challenge somewhere so you can have it
@@ -101,12 +102,12 @@ session[:creation_challenge] = options.challenge
101
102
  begin
102
103
  webauthn_credential = relying_party.verify_registration(
103
104
  params[:publicKeyCredential],
104
- params[:create_challenge]
105
+ session[:creation_challenge]
105
106
  )
106
107
 
107
108
  # Store Credential ID, Credential Public Key and Sign Count for future authentications
108
109
  user.credentials.create!(
109
- webauthn_id: webauthn_credential.id,
110
+ external_id: webauthn_credential.id,
110
111
  public_key: webauthn_credential.public_key,
111
112
  sign_count: webauthn_credential.sign_count
112
113
  )
@@ -120,7 +121,7 @@ end
120
121
  #### Initiation phase
121
122
 
122
123
  ```ruby
123
- options = relying_party.options_for_get(allow: user.credentials.map { |c| c.webauthn_id })
124
+ options = relying_party.options_for_authentication(allow: user.credentials.map { |c| c.webauthn_id })
124
125
 
125
126
  # Store the newly generated challenge somewhere so you can have it
126
127
  # for the verification phase.
@@ -148,9 +149,9 @@ begin
148
149
  webauthn_credential, stored_credential = relying_party.verify_authentication(
149
150
  params[:publicKeyCredential],
150
151
  session[:authentication_challenge]
151
- ) do
152
+ ) do |webauthn_credential|
152
153
  # the returned object needs to respond to #public_key and #sign_count
153
- user.credentials.find_by(webauthn_id: webauthn_credential.id)
154
+ user.credentials.find_by(external_id: webauthn_credential.id)
154
155
  end
155
156
 
156
157
  # Update the stored credential sign count with the value from `webauthn_credential.sign_count`
@@ -159,7 +160,7 @@ begin
159
160
  # Continue with successful sign in or 2FA verification...
160
161
 
161
162
  rescue WebAuthn::SignCountVerificationError => e
162
- # Cryptographic verification of the authenticator data succeeded, but the signature counter was less then or equal
163
+ # Cryptographic verification of the authenticator data succeeded, but the signature counter was less than or equal
163
164
  # to the stored value. This can have several reasons and depending on your risk tolerance you can choose to fail or
164
165
  # pass authentication. For more information see https://www.w3.org/TR/webauthn/#sign-counter
165
166
  rescue WebAuthn::Error => e
@@ -171,4 +172,4 @@ end
171
172
 
172
173
  Adding a configuration for a new instance does not mean you need to get rid of your Global configuration. They can co-exist in your application and be both available for the different usages you might have. `WebAuthn.configuration.relying_party` will always return the global one while `WebAuthn::RelyingParty.new`, executed anywhere in your codebase, will allow you to create a different instance as you see the need. They will not collide and instead operate in isolation without any shared state.
173
174
 
174
- The gem API described in the current [Usage](../README.md#usage) section for the [Global Configuration](../README.md#configuration) approach will still valid but the [Instance Based API](#instance-based-api) also works with the global `relying_party` that is maintain globally at `WebAuthn.configuration.relying_party`.
175
+ The gem API described in the current [Usage](../README.md#usage) section for the [Global Configuration](../README.md#configuration) approach will still be valid but the [Instance Based API](#instance-based-api) also works with the global `relying_party` that is maintained globally at `WebAuthn.configuration.relying_party`.
@@ -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
@@ -102,6 +102,15 @@ module WebAuthn
102
102
  end
103
103
 
104
104
  def valid_certificate_chain?(aaguid: nil, attestation_certificate_key_id: nil)
105
+ root_certificates = root_certificates(
106
+ aaguid: aaguid,
107
+ attestation_certificate_key_id: attestation_certificate_key_id
108
+ )
109
+
110
+ if certificates&.one? && root_certificates.include?(attestation_certificate)
111
+ return true
112
+ end
113
+
105
114
  attestation_root_certificates_store(
106
115
  aaguid: aaguid,
107
116
  attestation_certificate_key_id: attestation_certificate_key_id
@@ -37,8 +37,22 @@ module WebAuthn
37
37
  @user_handle = user_handle
38
38
  end
39
39
 
40
- def verify(expected_challenge, expected_origin = nil, public_key:, sign_count:, user_verification: nil, rp_id: nil)
41
- super(expected_challenge, expected_origin, user_verification: user_verification, rp_id: rp_id)
40
+ def verify(
41
+ expected_challenge,
42
+ expected_origin = nil,
43
+ public_key:,
44
+ sign_count:,
45
+ user_presence: nil,
46
+ user_verification: nil,
47
+ rp_id: nil
48
+ )
49
+ super(
50
+ expected_challenge,
51
+ expected_origin,
52
+ user_presence: user_presence,
53
+ user_verification: user_verification,
54
+ rp_id: rp_id
55
+ )
42
56
  verify_item(:signature, WebAuthn::PublicKey.deserialize(public_key))
43
57
  verify_item(:sign_count, sign_count)
44
58
 
@@ -12,7 +12,6 @@ require "webauthn/encoder"
12
12
 
13
13
  module WebAuthn
14
14
  class AttestationStatementVerificationError < VerificationError; end
15
- class AttestationTrustworthinessVerificationError < VerificationError; end
16
15
  class AttestedCredentialVerificationError < VerificationError; end
17
16
 
18
17
  class AuthenticatorAttestationResponse < AuthenticatorResponse
@@ -23,21 +22,23 @@ module WebAuthn
23
22
 
24
23
  new(
25
24
  attestation_object: encoder.decode(response["attestationObject"]),
25
+ transports: response["transports"],
26
26
  client_data_json: encoder.decode(response["clientDataJSON"]),
27
27
  relying_party: relying_party
28
28
  )
29
29
  end
30
30
 
31
- attr_reader :attestation_type, :attestation_trust_path
31
+ attr_reader :attestation_type, :attestation_trust_path, :transports
32
32
 
33
- def initialize(attestation_object:, **options)
33
+ def initialize(attestation_object:, transports: [], **options)
34
34
  super(**options)
35
35
 
36
36
  @attestation_object_bytes = attestation_object
37
+ @transports = transports
37
38
  @relying_party = relying_party
38
39
  end
39
40
 
40
- def verify(expected_challenge, expected_origin = nil, user_verification: nil, rp_id: nil)
41
+ def verify(expected_challenge, expected_origin = nil, user_presence: nil, user_verification: nil, rp_id: nil)
41
42
  super
42
43
 
43
44
  verify_item(:attested_credential)
@@ -24,8 +24,9 @@ module WebAuthn
24
24
  @relying_party = relying_party
25
25
  end
26
26
 
27
- def verify(expected_challenge, expected_origin = nil, user_verification: nil, rp_id: nil)
28
- expected_origin ||= relying_party.origin || raise("Unspecified expected origin")
27
+ def verify(expected_challenge, expected_origin = nil, user_presence: nil, user_verification: nil, rp_id: nil)
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,9 +34,14 @@ 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
37
 
38
- if !relying_party.silent_authentication
38
+ verify_item(
39
+ :rp_id,
40
+ rp_id || rp_id_from_origin(expected_origin)
41
+ )
42
+
43
+ # Fallback to RP configuration unless user_presence is passed in explicitely
44
+ if user_presence.nil? && !relying_party.silent_authentication || user_presence
39
45
  verify_item(:user_presence)
40
46
  end
41
47
 
@@ -83,10 +89,14 @@ module WebAuthn
83
89
  end
84
90
 
85
91
  def valid_origin?(expected_origin)
86
- expected_origin && (client_data.origin == expected_origin)
92
+ return false unless expected_origin
93
+
94
+ expected_origin.include?(client_data.origin)
87
95
  end
88
96
 
89
97
  def valid_rp_id?(rp_id)
98
+ return false unless rp_id
99
+
90
100
  OpenSSL::Digest::SHA256.digest(rp_id) == authenticator_data.rp_id_hash
91
101
  end
92
102
 
@@ -105,7 +115,7 @@ module WebAuthn
105
115
  end
106
116
 
107
117
  def rp_id_from_origin(expected_origin)
108
- URI.parse(expected_origin).host
118
+ URI.parse(expected_origin.first).host if expected_origin.size == 1
109
119
  end
110
120
 
111
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,