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.
- checksums.yaml +4 -4
- data/.github/actions/install-openssl/action.yml +55 -0
- data/.github/actions/install-ruby/action.yml +84 -0
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/build.yml +60 -10
- data/.github/workflows/git.yml +1 -1
- data/.rubocop.yml +1 -1
- data/CHANGELOG.md +77 -0
- data/README.md +5 -4
- data/docs/advanced_configuration.md +16 -15
- data/lib/webauthn/attestation_statement/base.rb +10 -1
- data/lib/webauthn/authenticator_assertion_response.rb +16 -2
- data/lib/webauthn/authenticator_attestation_response.rb +5 -4
- data/lib/webauthn/authenticator_response.rb +16 -6
- data/lib/webauthn/client_data.rb +4 -6
- data/lib/webauthn/configuration.rb +2 -0
- data/lib/webauthn/credential.rb +0 -1
- data/lib/webauthn/encoder.rb +5 -33
- data/lib/webauthn/encoders.rb +62 -0
- data/lib/webauthn/fake_authenticator/attestation_object.rb +10 -1
- data/lib/webauthn/fake_authenticator/authenticator_data.rb +3 -1
- data/lib/webauthn/fake_authenticator.rb +25 -4
- data/lib/webauthn/fake_client.rb +6 -1
- data/lib/webauthn/json_serializer.rb +45 -0
- data/lib/webauthn/public_key_credential/entity.rb +2 -24
- data/lib/webauthn/public_key_credential/options.rb +2 -24
- data/lib/webauthn/public_key_credential.rb +17 -2
- data/lib/webauthn/public_key_credential_with_assertion.rb +2 -1
- data/lib/webauthn/public_key_credential_with_attestation.rb +2 -2
- data/lib/webauthn/relying_party.rb +24 -7
- data/lib/webauthn/u2f_migrator.rb +5 -3
- data/lib/webauthn/version.rb +1 -1
- data/lib/webauthn.rb +1 -0
- data/webauthn.gemspec +5 -6
- metadata +26 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50b5c2c43f3e5719dd1ed0f63e9a7bb9705f2f1e97861ab0a0ee0a7d61c4ee41
|
4
|
+
data.tar.gz: d8849b387d30e54f7a45fd66bcb2b164983100b0b3369ca8fbf52840261e7fe4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 }}
|
data/.github/workflows/build.yml
CHANGED
@@ -7,26 +7,76 @@
|
|
7
7
|
|
8
8
|
name: build
|
9
9
|
|
10
|
-
on:
|
10
|
+
on:
|
11
|
+
push:
|
12
|
+
branches: [master]
|
13
|
+
pull_request:
|
14
|
+
types: [opened, synchronize]
|
11
15
|
|
12
16
|
jobs:
|
13
17
|
test:
|
14
|
-
|
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
|
-
|
22
|
-
- '
|
23
|
-
- '2
|
24
|
-
- '
|
25
|
-
-
|
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@
|
28
|
-
|
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
|
-
|
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
|
data/.github/workflows/git.yml
CHANGED
data/.rubocop.yml
CHANGED
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
|
-
|
2
|
-
For the current release version see https://github.com/cedarcode/webauthn-ruby/blob/
|
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
|

|
7
7
|
|
8
8
|
[](https://rubygems.org/gems/webauthn)
|
9
|
-
[](https://github.com/cedarcode/webauthn-ruby/actions/workflows/build.yml)
|
10
10
|
[](https://conventionalcommits.org)
|
11
11
|
[](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
|
-
|
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
|
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
|
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
|
-
|
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
|
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
|
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
|
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::
|
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.
|
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
|
-
|
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
|
-
|
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.
|
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(
|
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
|
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
|
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&.
|
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(
|
41
|
-
|
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.
|
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
|
-
|
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
|
-
|
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
|
data/lib/webauthn/client_data.rb
CHANGED
@@ -49,12 +49,10 @@ module WebAuthn
|
|
49
49
|
|
50
50
|
def data
|
51
51
|
@data ||=
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|