webauthn-rails 0.1.0 → 0.1.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 (15) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/erb/webauthn_authentication/templates/app/views/passkeys/new.html.erb.tt +2 -2
  3. data/lib/generators/test_unit/webauthn_authentication/templates/test/controllers/passkeys_controller_test.rb +16 -24
  4. data/lib/generators/test_unit/webauthn_authentication/templates/test/controllers/second_factor_authentications_controller_test.rb +131 -0
  5. data/lib/generators/test_unit/webauthn_authentication/templates/test/controllers/second_factor_webauthn_credentials_controller_test.rb +103 -0
  6. data/lib/generators/test_unit/webauthn_authentication/templates/test/controllers/webauthn_sessions_controller_test.rb +8 -10
  7. data/lib/generators/test_unit/webauthn_authentication/templates/test/system/manage_webauthn_credentials_test.rb +30 -30
  8. data/lib/generators/test_unit/webauthn_authentication/templates/test/test_helpers/virtual_authenticator_test_helper.rb +38 -0
  9. data/lib/generators/test_unit/webauthn_authentication/webauthn_authentication_generator.rb +2 -0
  10. data/lib/generators/webauthn_authentication/templates/app/controllers/passkeys_controller.rb +2 -2
  11. data/lib/generators/webauthn_authentication/templates/app/controllers/second_factor_authentications_controller.rb +3 -3
  12. data/lib/generators/webauthn_authentication/templates/app/controllers/second_factor_webauthn_credentials_controller.rb +2 -2
  13. data/lib/generators/webauthn_authentication/webauthn_authentication_generator.rb +7 -5
  14. data/lib/webauthn/rails/version.rb +1 -1
  15. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5bf8803deeac8d925eef701fa4b9a25b3ecea1288e5af2b07dad81e60b16574d
4
- data.tar.gz: ad28b85f03d4d693ec12d94c6702e6a8a6a0fbdd9226303efa520e61bc6c1000
3
+ metadata.gz: a8425de86118ec096864312cfb7b4ef98947437da28d88cdc5f12b0a4191aea9
4
+ data.tar.gz: 43f56151976719e1bb00200b3134cdde63b6786f5afaf225c274982e324258fc
5
5
  SHA512:
6
- metadata.gz: 7d10d95538f98bfd0375159c3bd7acba311992cf06062aecfae5fe1f1c4bdf57de6494fd6c5ade9e70a5d5e096be723a768d330ec3722fe1f659904f4842d834
7
- data.tar.gz: c81deb6084edc784f72d1cda4189e651a4748a75af71d80ad15e942a4ca6bd497f5f3211930b3416bb0ea474577fe7481850226938a9b725d1933bcbec652031
6
+ metadata.gz: d44fa7ccb7a3adc9248a74ab73f2758e0b3fb3fd4b7b70e98cb8956fac70c9c84b3eec954f39e4f261f26d8a4e3112bc4e4a289cab19542b2c11bbe4d4462d99
7
+ data.tar.gz: 784af4bb7538eccd87f50c4ce61d57ee8e3d92b823586c0c08c3c24718405b21d4258de6f599eca3e33b143a24ba1384fbc1365ec8e357186483dd708ef7686b
@@ -10,9 +10,9 @@
10
10
  "webauthn-credentials-options-url-value": create_options_passkeys_path,
11
11
  }) do |form| %>
12
12
  <div class="field">
13
- <%%= form.label :nickname, 'Security Key nickname' %>
13
+ <%%= form.label :nickname, 'Passkey nickname' %>
14
14
  <%%= form.text_field :nickname, required: true %>
15
15
  </div>
16
16
  <%%= form.hidden_field :public_key_credential, data: { "webauthn-credentials-target": "credentialHiddenInput" } %>
17
- <%%= form.submit "Add Security Key", disabled: true, data: { "webauthn-credentials-target": "submitButton" } %>
17
+ <%%= form.submit "Add Passkey", disabled: true, data: { "webauthn-credentials-target": "submitButton" } %>
18
18
  <%% end %>
@@ -7,7 +7,7 @@ class PasskeysControllerTest < ActionDispatch::IntegrationTest
7
7
  @client = WebAuthn::FakeClient.new(WebAuthn.configuration.allowed_origins.first)
8
8
  end
9
9
 
10
- test "initiates Passkey creation when user is authenticated" do
10
+ test "create_options" do
11
11
  sign_in_as @user
12
12
  post create_options_passkeys_url
13
13
 
@@ -20,14 +20,14 @@ class PasskeysControllerTest < ActionDispatch::IntegrationTest
20
20
  assert_equal session[:current_registration][:challenge], body["challenge"]
21
21
  end
22
22
 
23
- test "requires authentication to initiate Passkey creation" do
23
+ test "create_options unauthenticated" do
24
24
  post create_options_passkeys_url
25
25
 
26
26
  assert_response :redirect
27
27
  assert_redirected_to new_session_url
28
28
  end
29
29
 
30
- test "creates passkey when user is authenticated" do
30
+ test "create" do
31
31
  sign_in_as @user
32
32
 
33
33
  post create_options_passkeys_url
@@ -48,11 +48,11 @@ class PasskeysControllerTest < ActionDispatch::IntegrationTest
48
48
  end
49
49
 
50
50
  assert_redirected_to root_path
51
- assert_match (/Security Key registered successfully/), flash[:notice]
51
+ assert_match (/Passkey registered successfully/), flash[:notice]
52
52
  assert_nil session[:current_registration]
53
53
  end
54
54
 
55
- test "does not create passkey when there is a Webauthn error" do
55
+ test "create with WebAuthn error" do
56
56
  sign_in_as @user
57
57
 
58
58
  post create_options_passkeys_url
@@ -77,34 +77,26 @@ class PasskeysControllerTest < ActionDispatch::IntegrationTest
77
77
  assert_nil session[:current_registration]
78
78
  end
79
79
 
80
- test "requires authentication to create passkey" do
81
- post passkeys_url, params: {
82
- credential: {
83
- nickname: "My Passkey",
84
- public_key_credential: "{}"
85
- }
86
- }
80
+ test "create unauthenticated" do
81
+ post passkeys_url
87
82
 
88
83
  assert_response :redirect
89
84
  assert_redirected_to new_session_url
90
85
  end
91
86
 
92
- test "deletes passkey when user is authenticated" do
93
- 2.times do |i|
94
- WebauthnCredential.create!(
95
- nickname: "My Passkey #{i}",
96
- user: @user,
97
- external_id: "external-id-#{i}",
98
- public_key: "public-key-#{i}",
99
- sign_count: 0,
100
- authentication_factor: 0
101
- )
102
- end
87
+ test "destroy" do
88
+ credential = WebauthnCredential.passkey.create!(
89
+ nickname: "My Passkey",
90
+ user: @user,
91
+ external_id: "external-id",
92
+ public_key: "public-key",
93
+ sign_count: 0,
94
+ )
103
95
 
104
96
  sign_in_as @user
105
97
 
106
98
  assert_difference("WebauthnCredential.count", -1) do
107
- delete passkey_url(@user.webauthn_credentials.first)
99
+ delete passkey_url(credential)
108
100
  end
109
101
  assert_redirected_to root_path
110
102
  end
@@ -0,0 +1,131 @@
1
+ require "test_helper"
2
+ require "webauthn/fake_client"
3
+
4
+ class SecondFactorAuthenticationsControllerTest < ActionDispatch::IntegrationTest
5
+ setup do
6
+ @user = users(:one)
7
+ @client = WebAuthn::FakeClient.new(WebAuthn.configuration.allowed_origins.first)
8
+
9
+ creation_options = WebAuthn::Credential.options_for_create(
10
+ user: { id: @user.webauthn_id, name: @user.email_address },
11
+ authenticator_selection: { resident_key: "discouraged", user_verification: "discouraged" }
12
+ )
13
+ create_options = @client.create(challenge: creation_options.challenge)
14
+ credential = WebAuthn::Credential.from_create(create_options)
15
+
16
+ WebauthnCredential.second_factor.create!(
17
+ nickname: "My Security Key",
18
+ user: @user,
19
+ external_id: credential.id,
20
+ public_key: credential.public_key,
21
+ sign_count: 0
22
+ )
23
+ end
24
+
25
+ test "get_options" do
26
+ post session_path, params: { email_address: @user.email_address, password: "password" }
27
+
28
+ post get_options_second_factor_authentication_url
29
+
30
+ assert_response :success
31
+ body = JSON.parse(response.body)
32
+ assert body["challenge"].present?
33
+ assert body["userVerification"] == "discouraged"
34
+
35
+ assert_equal session[:current_authentication][:challenge], body["challenge"]
36
+ end
37
+
38
+ test "create" do
39
+ post session_path, params: { email_address: @user.email_address, password: "password" }
40
+
41
+ post get_options_second_factor_authentication_url
42
+ challenge = session[:current_authentication][:challenge]
43
+
44
+ public_key_credential = @client.get(challenge: challenge, user_verified: false)
45
+
46
+ post second_factor_authentication_url, params: {
47
+ session: {
48
+ public_key_credential: public_key_credential.to_json
49
+ }
50
+ }
51
+
52
+ assert_redirected_to root_path
53
+ assert_nil session[:current_authentication]
54
+ end
55
+
56
+ test "create with a passkey" do
57
+ client = WebAuthn::FakeClient.new(WebAuthn.configuration.allowed_origins.first)
58
+
59
+ creation_options = WebAuthn::Credential.options_for_create(
60
+ user: { id: @user.webauthn_id, name: @user.email_address },
61
+ authenticator_selection: { resident_key: "discouraged", user_verification: "discouraged" }
62
+ )
63
+ create_options = client.create(challenge: creation_options.challenge)
64
+ credential = WebAuthn::Credential.from_create(create_options)
65
+
66
+
67
+ WebauthnCredential.passkey.create!(
68
+ nickname: "My Security Key",
69
+ user: @user,
70
+ external_id: credential.id,
71
+ public_key: credential.public_key,
72
+ sign_count: 0
73
+ )
74
+
75
+ post session_path, params: { email_address: @user.email_address, password: "password" }
76
+
77
+ post get_options_second_factor_authentication_url
78
+ challenge = session[:current_authentication][:challenge]
79
+
80
+ public_key_credential = client.get(challenge: challenge, user_verified: false)
81
+
82
+ post second_factor_authentication_url, params: {
83
+ session: {
84
+ public_key_credential: public_key_credential.to_json
85
+ }
86
+ }
87
+
88
+ assert_redirected_to root_path
89
+ assert_nil session[:current_authentication]
90
+ end
91
+
92
+ test "create with WebAuthn error" do
93
+ post session_path, params: { email_address: @user.email_address, password: "password" }
94
+
95
+ post get_options_second_factor_authentication_url
96
+
97
+ public_key_credential = @client.get(
98
+ user_verified: false
99
+ )
100
+
101
+ post second_factor_authentication_url, params: {
102
+ session: {
103
+ public_key_credential: public_key_credential.to_json
104
+ }
105
+ }
106
+
107
+ assert_redirected_to new_second_factor_authentication_path
108
+ assert_match (/Verification failed/), flash[:alert]
109
+ assert_nil session[:current_authentication]
110
+ end
111
+
112
+ test "create with unrecognized credential" do
113
+ post session_path, params: { email_address: @user.email_address, password: "password" }
114
+
115
+ post get_options_second_factor_authentication_url
116
+ challenge = session[:current_authentication][:challenge]
117
+
118
+ public_key_credential = @client.get(challenge: challenge, user_verified: false)
119
+ public_key_credential["id"]= "invalid-id"
120
+
121
+ post second_factor_authentication_url, params: {
122
+ session: {
123
+ public_key_credential: public_key_credential.to_json
124
+ }
125
+ }
126
+
127
+ assert_redirected_to new_second_factor_authentication_path
128
+ assert_match (/Credential not recognized/), flash[:alert]
129
+ assert_nil session[:current_authentication]
130
+ end
131
+ end
@@ -0,0 +1,103 @@
1
+ require "test_helper"
2
+ require "webauthn/fake_client"
3
+
4
+ class SecondFactorWebauthnCredentialsControllerTest < ActionDispatch::IntegrationTest
5
+ setup do
6
+ @user = users(:one)
7
+ @client = WebAuthn::FakeClient.new(WebAuthn.configuration.allowed_origins.first)
8
+ end
9
+
10
+ test "create_options" do
11
+ sign_in_as @user
12
+ post create_options_second_factor_webauthn_credentials_url
13
+
14
+ assert_response :success
15
+ body = JSON.parse(response.body)
16
+ assert body["challenge"].present?
17
+ assert body["authenticatorSelection"]["residentKey"] == "discouraged"
18
+ assert body["authenticatorSelection"]["userVerification"] == "discouraged"
19
+
20
+ assert_equal session[:current_registration][:challenge], body["challenge"]
21
+ end
22
+
23
+ test "create_options unauthenticated" do
24
+ post create_options_second_factor_webauthn_credentials_url
25
+
26
+ assert_response :redirect
27
+ assert_redirected_to new_session_url
28
+ end
29
+
30
+ test "create" do
31
+ sign_in_as @user
32
+
33
+ post create_options_second_factor_webauthn_credentials_url
34
+ challenge = session[:current_registration][:challenge]
35
+
36
+ public_key_credential = @client.create(
37
+ challenge: challenge,
38
+ user_verified: false,
39
+ )
40
+
41
+ assert_difference("WebauthnCredential.second_factor.count", 1) do
42
+ post second_factor_webauthn_credentials_url, params: {
43
+ credential: {
44
+ nickname: "My Security Key",
45
+ public_key_credential: public_key_credential.to_json
46
+ }
47
+ }
48
+ end
49
+
50
+ assert_redirected_to root_path
51
+ assert_match (/Security Key registered successfully/), flash[:notice]
52
+ assert_nil session[:current_registration]
53
+ end
54
+
55
+ test "create with WebAuthn error" do
56
+ sign_in_as @user
57
+
58
+ post create_options_second_factor_webauthn_credentials_url
59
+
60
+ public_key_credential = @client.create(
61
+ user_verified: false,
62
+ )
63
+
64
+ assert_no_difference("WebauthnCredential.count") do
65
+ post second_factor_webauthn_credentials_url, params: {
66
+ credential: {
67
+ nickname: "My Security Key",
68
+ public_key_credential: public_key_credential.to_json
69
+ }
70
+ }
71
+ end
72
+
73
+ assert_redirected_to new_second_factor_webauthn_credential_path
74
+ assert_match (/Verification failed/), flash[:alert]
75
+ assert_nil session[:current_registration]
76
+ end
77
+
78
+ test "create unauthenticated" do
79
+ post second_factor_webauthn_credentials_url
80
+
81
+ assert_response :redirect
82
+ assert_redirected_to new_session_url
83
+ end
84
+
85
+ test "destroy" do
86
+ credential = WebauthnCredential.second_factor.create!(
87
+ user: @user,
88
+ nickname: "My Security Key",
89
+ external_id: "external_id",
90
+ public_key: "public_key",
91
+ sign_count: 0
92
+ )
93
+
94
+ sign_in_as @user
95
+
96
+ assert_difference("WebauthnCredential.second_factor.count", -1) do
97
+ delete second_factor_webauthn_credential_url(credential)
98
+ end
99
+
100
+ assert_redirected_to root_path
101
+ assert_match (/Security Key deleted successfully/), flash[:notice]
102
+ end
103
+ end
@@ -12,17 +12,16 @@ class WebauthnSessionsControllerTest < ActionDispatch::IntegrationTest
12
12
  create_options = @client.create(challenge: creation_options.challenge)
13
13
  credential = WebAuthn::Credential.from_create(create_options)
14
14
 
15
- WebauthnCredential.create!(
15
+ WebauthnCredential.passkey.create!(
16
16
  nickname: "My Passkey",
17
17
  user: @user,
18
18
  external_id: credential.id,
19
19
  public_key: credential.public_key,
20
20
  sign_count: 0,
21
- authentication_factor: 0
22
21
  )
23
22
  end
24
23
 
25
- test "should return get_options" do
24
+ test "get_options" do
26
25
  post get_options_webauthn_session_url
27
26
 
28
27
  assert_response :success
@@ -33,7 +32,7 @@ class WebauthnSessionsControllerTest < ActionDispatch::IntegrationTest
33
32
  assert_equal session[:current_authentication][:challenge], body["challenge"]
34
33
  end
35
34
 
36
- test "should create session with valid credential" do
35
+ test "create" do
37
36
  post get_options_webauthn_session_url
38
37
  challenge = session[:current_authentication][:challenge]
39
38
 
@@ -49,7 +48,7 @@ class WebauthnSessionsControllerTest < ActionDispatch::IntegrationTest
49
48
  assert_nil session[:current_authentication]
50
49
  end
51
50
 
52
- test "should not create session when there is a Webauthn error" do
51
+ test "create with WebAuthn error" do
53
52
  post get_options_webauthn_session_url
54
53
  challenge = session[:current_authentication][:challenge]
55
54
 
@@ -66,7 +65,7 @@ class WebauthnSessionsControllerTest < ActionDispatch::IntegrationTest
66
65
  assert_nil session[:current_authentication]
67
66
  end
68
67
 
69
- test "should not create session with unrecognized credential" do
68
+ test "create with unrecognized credential" do
70
69
  post get_options_webauthn_session_url
71
70
  challenge = session[:current_authentication][:challenge]
72
71
 
@@ -84,7 +83,7 @@ class WebauthnSessionsControllerTest < ActionDispatch::IntegrationTest
84
83
  assert_nil session[:current_authentication]
85
84
  end
86
85
 
87
- test "should not create session with a second factor credential" do
86
+ test "create with a second factor credential" do
88
87
  client = WebAuthn::FakeClient.new(WebAuthn.configuration.allowed_origins.first)
89
88
 
90
89
  creation_options = WebAuthn::Credential.options_for_create(
@@ -93,13 +92,12 @@ class WebauthnSessionsControllerTest < ActionDispatch::IntegrationTest
93
92
  create_options = client.create(challenge: creation_options.challenge)
94
93
  credential = WebAuthn::Credential.from_create(create_options)
95
94
 
96
- WebauthnCredential.create!(
95
+ WebauthnCredential.second_factor.create!(
97
96
  nickname: "Second Factor Key",
98
97
  user: @user,
99
98
  external_id: credential.id,
100
99
  public_key: credential.public_key,
101
100
  sign_count: 0,
102
- authentication_factor: 1
103
101
  )
104
102
 
105
103
  post get_options_webauthn_session_url
@@ -118,7 +116,7 @@ class WebauthnSessionsControllerTest < ActionDispatch::IntegrationTest
118
116
  assert_nil session[:current_authentication]
119
117
  end
120
118
 
121
- test "should destroy session" do
119
+ test "destroy" do
122
120
  delete webauthn_session_url
123
121
  assert_redirected_to new_session_path
124
122
  end
@@ -5,8 +5,7 @@ class ManageWebauthnCredentialsTest < ApplicationSystemTestCase
5
5
  include VirtualAuthenticatorTestHelper
6
6
 
7
7
  def setup
8
- user = User.create!(email_address: "alice@example.com", password: "S3cr3tP@ssw0rd!")
9
- sign_in_as(user)
8
+ @user = User.create!(email_address: "alice@example.com", password: "S3cr3tP@ssw0rd!")
10
9
  @authenticator = add_virtual_authenticator
11
10
  end
12
11
 
@@ -14,63 +13,64 @@ class ManageWebauthnCredentialsTest < ApplicationSystemTestCase
14
13
  @authenticator.remove!
15
14
  end
16
15
 
17
- test "add credentials and sign in" do
18
- visit root_path
16
+ test "adding a passkey" do
17
+ sign_in_as(@user)
19
18
 
19
+ visit new_passkey_path
20
+ fill_in("Passkey nickname", with: "Touch ID")
20
21
  click_on "Add Passkey"
21
22
 
22
- fill_in("Security Key nickname", with: "Touch ID")
23
- click_on "Add Security Key"
24
-
25
- assert_current_path "/"
26
- assert_selector "div", text: "Security Key registered successfully"
27
- assert_selector "span", text: "Touch ID"
23
+ assert_current_path root_path
24
+ # Add custom assertions based on your application's behavior
25
+ # assert_text "Passkey registered successfully"
26
+ end
28
27
 
29
- click_on "Sign out"
30
- assert_selector("input[type=submit][value='Sign in']")
28
+ test "signing in with existing passkey" do
29
+ add_passkey_to_authenticator(@authenticator, @user)
31
30
 
31
+ visit new_session_path
32
32
  click_on "Sign In with Passkey"
33
33
 
34
- assert_current_path "/"
35
- assert_selector "h3", text: "Your Passkeys"
34
+ assert_current_path root_path
35
+ # Add custom assertions based on your application's behavior
36
36
  end
37
37
 
38
- test "sign in with 2FA WebAuthn credential" do
39
- visit root_path
40
-
41
- click_on "Add Second Factor Key"
38
+ test "adding a 2FA WebAuthn credential" do
39
+ sign_in_as(@user)
42
40
 
41
+ visit new_second_factor_webauthn_credential_path
43
42
  fill_in("Security Key nickname", with: "Touch ID")
44
43
  click_on "Add Security Key"
45
44
 
46
- assert_current_path "/"
47
- assert_selector "div", text: "Security Key registered successfully"
48
- assert_selector "span", text: "Touch ID"
45
+ assert_current_path root_path
46
+ # Add custom assertions based on your application's behavior
47
+ # assert_text "Security Key registered successfully"
48
+ end
49
49
 
50
- click_on "Sign out"
51
- assert_selector("input[type=submit][value='Sign in']")
50
+ test "sign in with existing 2FA WebAuthn credential" do
51
+ add_security_key_to_authenticator(@authenticator, @user)
52
52
 
53
- fill_in "email_address", with: "alice@example.com"
54
- fill_in "password", with: "S3cr3tP@ssw0rd!"
53
+ visit new_session_path
54
+ fill_in "email_address", with: @user.email_address
55
+ fill_in "password", with: @user.password
55
56
  click_on "Sign in"
56
57
 
58
+ assert_current_path new_second_factor_authentication_path
57
59
  assert_selector "h3", text: "Two-factor authentication"
58
60
  click_on "Use Security Key"
59
61
 
60
- assert_current_path "/"
61
- assert_selector "h3", text: "Your Passkeys"
62
+ assert_current_path root_path
63
+ # Add custom assertions based on your application's behavior
62
64
  end
63
65
 
64
66
  private
65
67
 
66
68
  def sign_in_as(user)
67
69
  visit new_session_path
68
-
69
70
  fill_in "email_address", with: user.email_address
70
71
  fill_in "password", with: user.password
71
-
72
72
  click_on "Sign in"
73
73
 
74
- assert_selector "h3", text: "Your Passkeys"
74
+ assert_current_path root_path
75
75
  end
76
76
  end
@@ -6,4 +6,42 @@ module VirtualAuthenticatorTestHelper
6
6
  options.resident_key = true
7
7
  page.driver.browser.add_virtual_authenticator(options)
8
8
  end
9
+
10
+ def add_passkey_to_authenticator(authenticator, user)
11
+ add_credential_to_authenticator(authenticator, user, passkey: true)
12
+ end
13
+
14
+ def add_security_key_to_authenticator(authenticator, user)
15
+ add_credential_to_authenticator(authenticator, user, passkey: false)
16
+ end
17
+
18
+ def add_credential_to_authenticator(authenticator, user, passkey:)
19
+ credential_id = SecureRandom.random_bytes(16)
20
+ encoded_credential_id = Base64.urlsafe_encode64(credential_id)
21
+ key = OpenSSL::PKey.generate_key("ED25519")
22
+ encoded_private_key = Base64.urlsafe_encode64(key.private_to_der)
23
+
24
+ cose_public_key = COSE::Key::OKP.from_pkey(OpenSSL::PKey.read(key.public_to_der))
25
+ cose_public_key.alg = -8
26
+ encoded_cose_public_key = Base64.urlsafe_encode64(cose_public_key.serialize)
27
+
28
+ credential_json = {
29
+ "credentialId" => encoded_credential_id,
30
+ "isResidentCredential" => passkey,
31
+ "rpId" => "localhost",
32
+ "privateKey" => encoded_private_key,
33
+ "signCount" => 0
34
+ }
35
+ credential_json["userHandle"] = user.webauthn_id if passkey
36
+
37
+ authenticator.add_credential(credential_json)
38
+
39
+ user.webauthn_credentials.create!(
40
+ nickname: "My Credential",
41
+ external_id: Base64.urlsafe_encode64(credential_id, padding: false),
42
+ public_key: encoded_cose_public_key,
43
+ sign_count: 0,
44
+ authentication_factor: passkey ? :first_factor : :second_factor
45
+ )
46
+ end
9
47
  end
@@ -9,6 +9,8 @@ module TestUnit
9
9
  def create_controller_test_files
10
10
  template "test/controllers/passkeys_controller_test.rb"
11
11
  template "test/controllers/webauthn_sessions_controller_test.rb"
12
+ template "test/controllers/second_factor_authentications_controller_test.rb"
13
+ template "test/controllers/second_factor_webauthn_credentials_controller_test.rb"
12
14
  end
13
15
 
14
16
  def create_system_test_files
@@ -35,7 +35,7 @@ class PasskeysController < ApplicationController
35
35
  public_key: webauthn_credential.public_key,
36
36
  sign_count: webauthn_credential.sign_count
37
37
  )
38
- redirect_to root_path, notice: "Security Key registered successfully"
38
+ redirect_to root_path, notice: "Passkey registered successfully"
39
39
  else
40
40
  flash[:alert] = "Error registering credential"
41
41
  render :new
@@ -50,7 +50,7 @@ class PasskeysController < ApplicationController
50
50
  def destroy
51
51
  Current.user.passkeys.destroy(params[:id])
52
52
 
53
- redirect_to root_path, notice: "Security Key deleted successfully"
53
+ redirect_to root_path, notice: "Passkey deleted successfully"
54
54
  end
55
55
 
56
56
  private
@@ -35,9 +35,9 @@ class SecondFactorAuthenticationsController < ApplicationController
35
35
  redirect_to after_authentication_url
36
36
  rescue WebAuthn::Error => e
37
37
  redirect_to new_second_factor_authentication_path, alert: "Verification failed: #{e.message}"
38
- ensure
39
- session.delete(:current_authentication)
40
38
  end
39
+ ensure
40
+ session.delete(:current_authentication)
41
41
  end
42
42
 
43
43
  private
@@ -57,6 +57,6 @@ class SecondFactorAuthenticationsController < ApplicationController
57
57
  end
58
58
 
59
59
  def current_authentication_user_id
60
- session[:current_authentication][:user_id] || session[:current_authentication]["user_id"]
60
+ session.dig(:current_authentication, :user_id) || session.dig(:current_authentication, "user_id")
61
61
  end
62
62
  end
@@ -40,9 +40,9 @@ class SecondFactorWebauthnCredentialsController < ApplicationController
40
40
  end
41
41
  rescue WebAuthn::Error => e
42
42
  redirect_to new_second_factor_webauthn_credential_path, alert: "Verification failed: #{e.message}"
43
- ensure
44
- session.delete(:current_registration)
45
43
  end
44
+ ensure
45
+ session.delete(:current_registration)
46
46
  end
47
47
 
48
48
  def destroy
@@ -8,7 +8,11 @@ end
8
8
 
9
9
  class WebauthnAuthenticationGenerator < ::Rails::Generators::Base
10
10
  include ActiveRecord::Generators::Migration
11
- include BundleHelper
11
+ if Rails.version >= "8.1"
12
+ include Rails::Generators::BundleHelper
13
+ else
14
+ include BundleHelper
15
+ end
12
16
 
13
17
  source_root File.expand_path("../templates", __FILE__)
14
18
 
@@ -28,7 +32,7 @@ class WebauthnAuthenticationGenerator < ::Rails::Generators::Base
28
32
  if File.exist?(File.join(destination_root, "app/controllers/sessions_controller.rb"))
29
33
  gsub_file "app/controllers/sessions_controller.rb",
30
34
  /^ def create.*?^ end/m,
31
- <<~RUBY.strip_heredoc.indent(2)
35
+ <<~RUBY.chomp.indent(2)
32
36
  def create
33
37
  if user = User.authenticate_by(params.permit(:email_address, :password))
34
38
  if user.second_factor_enabled?
@@ -54,9 +58,7 @@ class WebauthnAuthenticationGenerator < ::Rails::Generators::Base
54
58
  <<-RUBY.strip_heredoc.indent(4)
55
59
 
56
60
  def require_no_authentication
57
- if Current.user
58
- redirect_to root_path
59
- end
61
+ redirect_to root_path if find_session_by_cookie
60
62
  end
61
63
  RUBY
62
64
  end
@@ -1,5 +1,5 @@
1
1
  module Webauthn
2
2
  module Rails
3
- VERSION = "0.1.0"
3
+ VERSION = "0.1.2"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webauthn-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cedarcode
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-09-26 00:00:00.000000000 Z
11
+ date: 2025-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -41,6 +41,8 @@ files:
41
41
  - lib/generators/erb/webauthn_authentication/templates/app/views/second_factor_webauthn_credentials/new.html.erb.tt
42
42
  - lib/generators/erb/webauthn_authentication/webauthn_authentication_generator.rb
43
43
  - lib/generators/test_unit/webauthn_authentication/templates/test/controllers/passkeys_controller_test.rb
44
+ - lib/generators/test_unit/webauthn_authentication/templates/test/controllers/second_factor_authentications_controller_test.rb
45
+ - lib/generators/test_unit/webauthn_authentication/templates/test/controllers/second_factor_webauthn_credentials_controller_test.rb
44
46
  - lib/generators/test_unit/webauthn_authentication/templates/test/controllers/webauthn_sessions_controller_test.rb
45
47
  - lib/generators/test_unit/webauthn_authentication/templates/test/system/manage_webauthn_credentials_test.rb
46
48
  - lib/generators/test_unit/webauthn_authentication/templates/test/test_helpers/virtual_authenticator_test_helper.rb