devise-webauthn 0.2.1 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8083a823cce7edf858475a062ea009193c3a86cf9b4a821db1dd2715430580d4
4
- data.tar.gz: df00de569113a642c13f87cc4f6c93836acb5c5df1ef1cecfe492b01247a17aa
3
+ metadata.gz: bf586d5c54c83dcd4dd18c38e2dfe2c8d16cd86dc90e3c5c09b516d3c60dff04
4
+ data.tar.gz: 5a56dc38f1bd836b69dae7057f44ba95868b1edbfe5820eab77466ec2db59ffd
5
5
  SHA512:
6
- metadata.gz: ce764390d121fc68bd1732d655700ccae382ced384a24f350256f2bb633910d0038972637a8e9f4f5e512d0e13030c53851f7288733fb233e8d86fe00ab5c8b8
7
- data.tar.gz: d2072d245253437be40005c81b9879f22f0a7c0fa3e0e3cd39b9b03f812aa05cdcd7404cbd4123afac473785364472f3e025dbdf025ae3f74af117854dcbca38
6
+ metadata.gz: c62d4d71fd255dbf5867c76a524ed2b8e22df167e537e52a928f5a88351291ccb4cc74aef5e0466b27c42242d6b0ab105e67acc2ba9d7cb673625ff0b31100b4
7
+ data.tar.gz: fefeab5ad1470d5a2eb3f451c823763f237c8fe3f302f46d02917bbf337a0ab3011fc518b209d6105aed7ca23c21cb8ef9621f360a6a980204d3fe28508cf552
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
@@ -8,7 +8,7 @@ jobs:
8
8
 
9
9
  steps:
10
10
  - name: Check out repository code
11
- uses: actions/checkout@v5
11
+ uses: actions/checkout@v6
12
12
 
13
13
  - name: Set up Ruby
14
14
  uses: ruby/setup-ruby@v1
@@ -69,7 +69,7 @@ jobs:
69
69
 
70
70
  steps:
71
71
  - name: Check out repository code
72
- uses: actions/checkout@v5
72
+ uses: actions/checkout@v6
73
73
 
74
74
  - name: Set up Ruby
75
75
  uses: ruby/setup-ruby@v1
data/.rubocop.yml CHANGED
@@ -7,6 +7,7 @@ AllCops:
7
7
  NewCops: enable
8
8
  Exclude:
9
9
  - "spec/internal/**/*"
10
+ - "spec/tmp/**/*"
10
11
  - "vendor/**/*"
11
12
  - "gemfiles/**/*"
12
13
  - "lib/devise/strategies/database_authenticatable.rb"
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## [v0.2.2](https://github.com/cedarcode/devise-webauthn/compare/v0.2.1...v0.2.2/) - 2025-12-11
6
+
7
+ - Generate webauthn credentials table with not null constraints in attributes that must be present.
8
+ - Update controllers and views generators to generate 2FA-related controllers and views.
9
+ - Add flash messages when removing credentials.
10
+
5
11
  ## [v0.2.1](https://github.com/cedarcode/devise-webauthn/compare/v0.2.0...v0.2.1/) - 2025-12-10
6
12
 
7
13
  - Add form helpers for security key registration and 2FA authentication.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- devise-webauthn (0.2.1)
4
+ devise-webauthn (0.2.2)
5
5
  devise (~> 4.9)
6
6
  webauthn (~> 3.0)
7
7
 
@@ -23,7 +23,12 @@ module Devise
23
23
  end
24
24
 
25
25
  def destroy
26
- resource.passkeys.destroy(params[:id])
26
+ if resource.passkeys.destroy(params[:id])
27
+ set_flash_message! :notice, :passkey_deleted
28
+ else
29
+ set_flash_message! :alert, :passkey_deletion_failed, scope: :"devise.failure"
30
+ end
31
+
27
32
  redirect_to after_update_path
28
33
  end
29
34
 
@@ -23,7 +23,12 @@ module Devise
23
23
  end
24
24
 
25
25
  def destroy
26
- resource.second_factor_webauthn_credentials.destroy(params[:id])
26
+ if resource.second_factor_webauthn_credentials.destroy(params[:id])
27
+ set_flash_message! :notice, :security_key_deleted
28
+ else
29
+ set_flash_message! :alert, :security_key_deletion_failed, scope: :"devise.failure"
30
+ end
31
+
27
32
  redirect_to after_update_path
28
33
  end
29
34
 
@@ -2,11 +2,15 @@ en:
2
2
  devise:
3
3
  passkeys:
4
4
  passkey_created: "Passkey created successfully."
5
+ passkey_deleted: "Passkey deleted successfully."
5
6
  second_factor_webauthn_credentials:
6
7
  security_key_created: "Security Key created successfully."
8
+ security_key_deleted: "Security Key deleted successfully."
7
9
  failure:
8
10
  passkey_not_found: "Your passkey doesn't exist or is not valid."
9
11
  passkey_verification_failed: "Passkey verification failed."
12
+ passkey_deletion_failed: "Passkey deletion failed."
13
+ security_key_deletion_failed: "Security Key deletion failed."
10
14
  sign_in_not_initiated: "Sign in was not initiated."
11
15
  two_factor_required: "Two-factor authentication is required to sign in."
12
16
  webauthn_credential_not_found: "Your WebAuthn credential doesn't exist or is not valid."
@@ -11,6 +11,48 @@ module Devise
11
11
  options.resident_key = true
12
12
  page.driver.browser.add_virtual_authenticator(options)
13
13
  end
14
+
15
+ def add_passkey_to_authenticator(authenticator, resource)
16
+ add_credential_to_authenticator(authenticator, resource, passkey: true)
17
+ end
18
+
19
+ def add_security_key_to_authenticator(authenticator, resource)
20
+ add_credential_to_authenticator(authenticator, resource, passkey: false)
21
+ end
22
+
23
+ # rubocop:disable Metrics/AbcSize
24
+ # rubocop:disable Metrics/MethodLength
25
+ def add_credential_to_authenticator(authenticator, resource, passkey:)
26
+ credential_id = SecureRandom.random_bytes(16)
27
+ encoded_credential_id = Base64.urlsafe_encode64(credential_id)
28
+ key = OpenSSL::PKey.generate_key("ED25519")
29
+ encoded_private_key = Base64.urlsafe_encode64(key.private_to_der)
30
+
31
+ cose_public_key = COSE::Key::OKP.from_pkey(OpenSSL::PKey.read(key.public_to_der))
32
+ cose_public_key.alg = -8
33
+ encoded_cose_public_key = Base64.urlsafe_encode64(cose_public_key.serialize)
34
+
35
+ credential_json = {
36
+ "credentialId" => encoded_credential_id,
37
+ "isResidentCredential" => passkey,
38
+ "rpId" => "localhost",
39
+ "privateKey" => encoded_private_key,
40
+ "signCount" => 0
41
+ }
42
+ credential_json["userHandle"] = resource.webauthn_id if passkey
43
+
44
+ authenticator.add_credential(credential_json)
45
+
46
+ resource.webauthn_credentials.create!(
47
+ name: "My Credential",
48
+ external_id: Base64.urlsafe_encode64(credential_id, padding: false),
49
+ public_key: encoded_cose_public_key,
50
+ sign_count: 0,
51
+ authentication_factor: passkey ? :first_factor : :second_factor
52
+ )
53
+ end
54
+ # rubocop:enable Metrics/MethodLength
55
+ # rubocop:enable Metrics/AbcSize
14
56
  end
15
57
  end
16
58
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Devise
4
4
  module Webauthn
5
- VERSION = "0.2.1"
5
+ VERSION = "0.2.2"
6
6
  end
7
7
  end
@@ -5,7 +5,11 @@ require "rails/generators"
5
5
  module Devise
6
6
  module Webauthn
7
7
  class ControllersGenerator < Rails::Generators::Base
8
- CONTROLLERS = %w[passkeys].freeze
8
+ CONTROLLERS = %w[
9
+ passkeys
10
+ second_factor_webauthn_credentials
11
+ two_factor_authentications
12
+ ].freeze
9
13
 
10
14
  desc "Create inherited Devise::Webauthn controllers in your app/controllers folder."
11
15
 
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= @scope_prefix %>SecondFactorWebauthnCredentialsController < Devise::SecondFactorWebauthnCredentialsController
4
+ # GET /resource/second_factor_webauthn_credentials/new
5
+ # def new
6
+ # super
7
+ # end
8
+
9
+ # POST /second_factor_webauthn_credentials/passkeys
10
+ # def create
11
+ # super
12
+ # end
13
+
14
+ # DELETE /resource/second_factor_webauthn_credentials/:id
15
+ # def destroy
16
+ # super
17
+ # end
18
+
19
+ # private
20
+
21
+ # Verifies the security key coming in the params and saves it to the database.
22
+ # def verify_and_save_security_key(security_key_from_params)
23
+ # super
24
+ # end
25
+
26
+ # The default url to be used after creating a second factor key. You can overwrite
27
+ # this method in your own SecondFactorWebauthnCredentialsController.
28
+ # def after_update_path
29
+ # super
30
+ # end
31
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= @scope_prefix %>TwoFactorAuthenticationsController < Devise::TwoFactorAuthenticationsController
4
+ # GET /resource/two_factor_authentication/new
5
+ # def new
6
+ # super
7
+ # end
8
+
9
+ # POST /resource/two_factor_authentication
10
+ # def create
11
+ # super
12
+ # end
13
+ end
@@ -22,7 +22,9 @@ module Devise
22
22
  end
23
23
  else
24
24
  view_directory :passkeys
25
+ view_directory :second_factor_webauthn_credentials
25
26
  view_directory :sessions
27
+ view_directory :two_factor_authentications
26
28
  end
27
29
  end
28
30
 
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateWebauthnCredentials < ActiveRecord::Migration[<%= Rails.version.to_f %>]
4
+ def change
5
+ create_table :webauthn_credentials do |t|
6
+ t.string :external_id, null: false
7
+ t.string :name, null: false
8
+ t.text :public_key, null: false
9
+ t.integer :sign_count, limit: 8, null: false
10
+ t.references :<%= user_model_name %>, null: false, foreign_key: true
11
+ t.integer :authentication_factor, limit: 1, null: false
12
+
13
+ t.timestamps
14
+ end
15
+ add_index :webauthn_credentials, :external_id, unique: true
16
+ end
17
+ end
@@ -6,22 +6,28 @@ require "rails/generators/active_record"
6
6
  module Devise
7
7
  module Webauthn
8
8
  class WebauthnCredentialModelGenerator < Rails::Generators::Base
9
+ include Rails::Generators::Migration
10
+
9
11
  hide!
10
12
  namespace "devise:webauthn:webauthn_credential_model"
11
13
 
14
+ source_root File.expand_path("templates", __dir__)
15
+
12
16
  desc "Generate a WebauthnCredential model with the required fields for WebAuthn"
13
17
  class_option :resource_name, type: :string, default: "user", desc: "The resource name for Devise (default: user)"
14
18
 
19
+ def self.next_migration_number(dirname)
20
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
21
+ end
22
+
15
23
  def generate_model
16
- invoke "active_record:model", [
17
- "webauthn_credential",
18
- "external_id:string:uniq",
19
- "name:string",
20
- "public_key:text",
21
- "sign_count:integer{8}",
22
- "#{user_model_name}:references",
23
- "authentication_factor:integer{1}"
24
- ]
24
+ invoke "active_record:model", ["webauthn_credential"], migration: false
25
+ end
26
+
27
+ # TODO: Remove this in favor of strandard model generator with
28
+ # not null modifier (`!`) once we drop support for Rails < 8.
29
+ def generate_migration
30
+ migration_template "webauthn_credential_migration.rb.erb", "db/migrate/create_webauthn_credentials.rb"
25
31
  end
26
32
 
27
33
  def inject_webauthn_credential_content
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: devise-webauthn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cedarcode
@@ -44,6 +44,7 @@ extensions: []
44
44
  extra_rdoc_files: []
45
45
  files:
46
46
  - ".github/FUNDING.yml"
47
+ - ".github/dependabot.yml"
47
48
  - ".github/workflows/release.yml"
48
49
  - ".github/workflows/ruby.yml"
49
50
  - ".gitignore"
@@ -95,7 +96,10 @@ files:
95
96
  - lib/generators/devise/webauthn/stimulus/templates/webauthn_credentials_controller.js
96
97
  - lib/generators/devise/webauthn/templates/controllers/README
97
98
  - lib/generators/devise/webauthn/templates/controllers/passkeys_controller.rb.tt
99
+ - lib/generators/devise/webauthn/templates/controllers/second_factor_webauthn_credentials_controller.rb.tt
100
+ - lib/generators/devise/webauthn/templates/controllers/two_factor_authentications_controller.rb.tt
98
101
  - lib/generators/devise/webauthn/views_generator.rb
102
+ - lib/generators/devise/webauthn/webauthn_credential_model/templates/webauthn_credential_migration.rb.erb
99
103
  - lib/generators/devise/webauthn/webauthn_credential_model/webauthn_credential_model_generator.rb
100
104
  - lib/generators/devise/webauthn/webauthn_id/webauthn_id_generator.rb
101
105
  homepage: https://github.com/cedarcode/devise-webauthn