passkeys-rails 0.2.0 → 0.3.0
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/CHANGELOG.md +11 -0
- data/README.md +37 -2
- data/app/controllers/passkeys_rails/application_controller.rb +5 -0
- data/app/controllers/passkeys_rails/passkeys_controller.rb +41 -7
- data/app/interactors/passkeys_rails/debug_login.rb +2 -12
- data/app/interactors/passkeys_rails/debug_register.rb +44 -0
- data/app/interactors/passkeys_rails/finish_authentication.rb +1 -0
- data/app/interactors/passkeys_rails/finish_registration.rb +17 -61
- data/app/interactors/passkeys_rails/refresh_token.rb +1 -0
- data/app/models/concerns/passkeys_rails/authenticatable.rb +1 -0
- data/app/models/concerns/passkeys_rails/authenticatable_creator.rb +53 -0
- data/app/models/concerns/passkeys_rails/debuggable.rb +19 -0
- data/config/routes.rb +7 -6
- data/lib/generators/passkeys_rails/templates/passkeys_rails_config.rb +15 -0
- data/lib/passkeys-rails.rb +32 -1
- data/lib/passkeys_rails/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: afbb635c3ddfd36d60bccbf936e2a67e091a6bb15822683c9c1db050cdd49909
|
4
|
+
data.tar.gz: c9ef1cf6d9e9f5a760037efb831b856dbf706c0860fbf945eadd616c37cb297d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 92e7f2a72715c7c4b313d57f7dc1c928cf334375fcdd28fc104f86721562b45484220234e8e5924106fce344e94d95f9cb7e29d0d6879e4ac73451e7a1a17915
|
7
|
+
data.tar.gz: 686d6a95cec8274eaadd3fa1555a89f0e1554bc248c1f30eca1ac253933a158e8d12e08bcd74e53b42cbe33a9e0a1630ed14d1ce3a7205a2fe289366d3cbe2c3
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
### 0.3.0
|
2
|
+
|
3
|
+
* Added debug_register endpoint.
|
4
|
+
* Fixed authenticatable_params for register enpoint.
|
5
|
+
* Added notifications to certain controller actions.
|
6
|
+
* Improved spec error helper.
|
7
|
+
|
8
|
+
### 0.2.1
|
9
|
+
|
10
|
+
Added ability to pass either the auth token string or a request with one in the header to authenticate methods.
|
11
|
+
|
1
12
|
### 0.2.0
|
2
13
|
|
3
14
|
* Added passkeys/debug_login functionality.
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
[](https://badge.fury.io/rb/passkeys-rails)
|
2
2
|
[](https://travis-ci.org/alliedcode/passkeys-rails)
|
3
3
|
[](https://codecov.io/gh/alliedcode/passkeys-rails)
|
4
4
|
|
@@ -56,7 +56,7 @@ Finally, execute:
|
|
56
56
|
$ rails generate passkeys_rails:install
|
57
57
|
```
|
58
58
|
|
59
|
-
This will add the `passkeys_rails.rb` configuration file, passkeys routes, and a couple of database migrations to your project.
|
59
|
+
This will add the `config/initializers/passkeys_rails.rb` configuration file, passkeys routes, and a couple of database migrations to your project.
|
60
60
|
|
61
61
|
### Adding to an standard rails project
|
62
62
|
|
@@ -113,6 +113,41 @@ This will add the `passkeys_rails.rb` configuration file, passkeys routes, and a
|
|
113
113
|
|
114
114
|
To access the currently authenticated entity, use `current_agent`. If you associated the registration of the agent with one of your own models, use `current_agent.authenticatable`. For example, if you associated the `User` class with the registration, `current_agent.authenticatable` will be a User object.
|
115
115
|
|
116
|
+
### Notifications
|
117
|
+
|
118
|
+
Certain actions trigger notifications that can be subscribed. See `subscribe` in `passkeys_rails.rb`.
|
119
|
+
|
120
|
+
#### Events
|
121
|
+
|
122
|
+
- `:did_register ` - a new agent has registered
|
123
|
+
|
124
|
+
- `:did_authenticate` - an agent has been authenticated
|
125
|
+
|
126
|
+
- `:did_refresh` - an agent's auth token has been refreshed
|
127
|
+
|
128
|
+
A convenient place to set these up in is in `passkeys_rails.rb`
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
PasskeysRails.config do |c|
|
132
|
+
c.subscribe(:did_register) do |event, agent, request|
|
133
|
+
# do something with the agent and/or request
|
134
|
+
end
|
135
|
+
|
136
|
+
c.subscribe(:did_authenticate) do |event, agent, request|
|
137
|
+
# do something with the agent and/or request
|
138
|
+
end
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
Subscriptions can also be done elsewhere as subscribe is a PasskeysRails class method.
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
PasskeysRails.subscribe(:did_register) do |event, agent, request|
|
146
|
+
# do something with the agent and/or request
|
147
|
+
end
|
148
|
+
```
|
149
|
+
|
150
|
+
|
116
151
|
### Authentication Failure
|
117
152
|
|
118
153
|
1. In the event of authentication failure, PasskeysRails returns an error code and message.
|
@@ -1,10 +1,15 @@
|
|
1
1
|
module PasskeysRails
|
2
2
|
class ApplicationController < ActionController::Base
|
3
|
+
rescue_from StandardError, with: :handle_standard_error
|
3
4
|
rescue_from ::Interactor::Failure, with: :handle_interactor_failure
|
4
5
|
rescue_from ActionController::ParameterMissing, with: :handle_missing_parameter
|
5
6
|
|
6
7
|
protected
|
7
8
|
|
9
|
+
def handle_standard_error(error)
|
10
|
+
render_error(:authentication, 'error', error.message.truncate(512), status: 500)
|
11
|
+
end
|
12
|
+
|
8
13
|
def handle_missing_parameter(error)
|
9
14
|
render_error(:authentication, 'missing_parameter', error.message)
|
10
15
|
end
|
@@ -1,5 +1,8 @@
|
|
1
1
|
module PasskeysRails
|
2
2
|
class PasskeysController < ApplicationController
|
3
|
+
skip_before_action :verify_authenticity_token
|
4
|
+
wrap_parameters false
|
5
|
+
|
3
6
|
def challenge
|
4
7
|
result = PasskeysRails::BeginChallenge.call!(username: challenge_params[:username])
|
5
8
|
|
@@ -11,23 +14,30 @@ module PasskeysRails
|
|
11
14
|
|
12
15
|
def register
|
13
16
|
result = PasskeysRails::FinishRegistration.call!(credential: attestation_credential_params.to_h,
|
14
|
-
authenticatable_info:
|
17
|
+
authenticatable_info: authenticatable_params&.to_h,
|
15
18
|
username: session.dig(:passkeys_rails, :username),
|
16
19
|
challenge: session.dig(:passkeys_rails, :challenge))
|
17
20
|
|
18
|
-
|
21
|
+
broadcast(:did_register, agent: result.agent)
|
22
|
+
|
23
|
+
render json: auth_response(result)
|
19
24
|
end
|
20
25
|
|
21
26
|
def authenticate
|
22
27
|
result = PasskeysRails::FinishAuthentication.call!(credential: authentication_params.to_h,
|
23
28
|
challenge: session.dig(:passkeys_rails, :challenge))
|
24
29
|
|
25
|
-
|
30
|
+
broadcast(:did_authenticate, agent: result.agent)
|
31
|
+
|
32
|
+
render json: auth_response(result)
|
26
33
|
end
|
27
34
|
|
28
35
|
def refresh
|
29
36
|
result = PasskeysRails::RefreshToken.call!(token: refresh_params[:auth_token])
|
30
|
-
|
37
|
+
|
38
|
+
broadcast(:did_refresh, agent: result.agent)
|
39
|
+
|
40
|
+
render json: auth_response(result)
|
31
41
|
end
|
32
42
|
|
33
43
|
# This action exists to allow easier mobile app debugging as it may not
|
@@ -36,11 +46,31 @@ module PasskeysRails
|
|
36
46
|
# CAUTION: It is very insecure to set DEBUG_LOGIN_REGEX in a production environment.
|
37
47
|
def debug_login
|
38
48
|
result = PasskeysRails::DebugLogin.call!(username: debug_login_params[:username])
|
39
|
-
|
49
|
+
|
50
|
+
broadcast(:did_authenticate, agent: result.agent)
|
51
|
+
|
52
|
+
render json: auth_response(result)
|
53
|
+
end
|
54
|
+
|
55
|
+
# This action exists to allow easier mobile app debugging as it may not
|
56
|
+
# be possible to acess Passkey functionality in mobile simulators.
|
57
|
+
# It is only routable if DEBUG_LOGIN_REGEX is set in the server environment.
|
58
|
+
# CAUTION: It is very insecure to set DEBUG_LOGIN_REGEX in a production environment.
|
59
|
+
def debug_register
|
60
|
+
result = PasskeysRails::DebugRegister.call!(username: debug_login_params[:username],
|
61
|
+
authenticatable_info: authenticatable_params&.to_h)
|
62
|
+
|
63
|
+
broadcast(:did_register, agent: result.agent)
|
64
|
+
|
65
|
+
render json: auth_response(result)
|
40
66
|
end
|
41
67
|
|
42
68
|
protected
|
43
69
|
|
70
|
+
def auth_response(result)
|
71
|
+
{ username: result.username, auth_token: result.auth_token }
|
72
|
+
end
|
73
|
+
|
44
74
|
def challenge_params
|
45
75
|
params.permit(:username)
|
46
76
|
end
|
@@ -52,8 +82,8 @@ module PasskeysRails
|
|
52
82
|
credential.permit(:id, :rawId, :type, { response: %i[attestationObject clientDataJSON] })
|
53
83
|
end
|
54
84
|
|
55
|
-
def
|
56
|
-
params.require
|
85
|
+
def authenticatable_params
|
86
|
+
params.require(:authenticatable).permit(:class, params: {}) if params[:authenticatable].present?
|
57
87
|
end
|
58
88
|
|
59
89
|
def authentication_params
|
@@ -71,5 +101,9 @@ module PasskeysRails
|
|
71
101
|
params.require(:username)
|
72
102
|
params.permit(:username)
|
73
103
|
end
|
104
|
+
|
105
|
+
def broadcast(event_name, agent:)
|
106
|
+
ActiveSupport::Notifications.instrument("passkeys_rails.#{event_name}", { agent:, request: })
|
107
|
+
end
|
74
108
|
end
|
75
109
|
end
|
@@ -5,6 +5,7 @@
|
|
5
5
|
module PasskeysRails
|
6
6
|
class DebugLogin
|
7
7
|
include Interactor
|
8
|
+
include Debuggable
|
8
9
|
|
9
10
|
delegate :username, to: :context
|
10
11
|
|
@@ -12,6 +13,7 @@ module PasskeysRails
|
|
12
13
|
ensure_debug_mode
|
13
14
|
ensure_regex_match
|
14
15
|
|
16
|
+
context.agent = agent
|
15
17
|
context.username = agent.username
|
16
18
|
context.auth_token = GenerateAuthToken.call!(agent:).auth_token
|
17
19
|
rescue Interactor::Failure => e
|
@@ -20,18 +22,6 @@ module PasskeysRails
|
|
20
22
|
|
21
23
|
private
|
22
24
|
|
23
|
-
def ensure_debug_mode
|
24
|
-
context.fail!(code: :not_allowed, message: 'Action not allowed') if username_regex.blank?
|
25
|
-
end
|
26
|
-
|
27
|
-
def ensure_regex_match
|
28
|
-
context.fail!(code: :not_allowed, message: 'Invalid username') unless username&.match?(username_regex)
|
29
|
-
end
|
30
|
-
|
31
|
-
def username_regex
|
32
|
-
PasskeysRails.debug_login_regex
|
33
|
-
end
|
34
|
-
|
35
25
|
def agent
|
36
26
|
@agent ||= begin
|
37
27
|
agent = Agent.find_by(username:)
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# This functionality exists to allow easier mobile app debugging as it may not
|
2
|
+
# be possible to acess Passkey functionality in mobile simulators.
|
3
|
+
# It is only operational if DEBUG_LOGIN_REGEX is set in the server environment.
|
4
|
+
# CAUTION: It is very insecure to set DEBUG_LOGIN_REGEX in a production environment.
|
5
|
+
module PasskeysRails
|
6
|
+
class DebugRegister
|
7
|
+
include Interactor
|
8
|
+
include Debuggable
|
9
|
+
include AuthenticatableCreator
|
10
|
+
|
11
|
+
delegate :username, :authenticatable_info, to: :context
|
12
|
+
|
13
|
+
def call
|
14
|
+
ensure_debug_mode
|
15
|
+
ensure_regex_match
|
16
|
+
|
17
|
+
ActiveRecord::Base.transaction do
|
18
|
+
create_authenticatable! if aux_class_name.present?
|
19
|
+
end
|
20
|
+
|
21
|
+
context.agent = agent
|
22
|
+
context.username = agent.username
|
23
|
+
context.auth_token = GenerateAuthToken.call!(agent:).auth_token
|
24
|
+
rescue Interactor::Failure => e
|
25
|
+
context.fail! code: e.context.code, message: e.context.message
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def agent
|
31
|
+
@agent ||= begin
|
32
|
+
result = BeginRegistration.call(username:)
|
33
|
+
context.fail!(code: result.code, message: result.message) if result.failure?
|
34
|
+
|
35
|
+
agent = Agent.find_by(username:)
|
36
|
+
context.fail!(code: :agent_not_found, message: "No agent found with that username") if agent.blank?
|
37
|
+
|
38
|
+
agent.update! registered_at: Time.current
|
39
|
+
|
40
|
+
agent
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -2,13 +2,19 @@
|
|
2
2
|
module PasskeysRails
|
3
3
|
class FinishRegistration
|
4
4
|
include Interactor
|
5
|
+
include AuthenticatableCreator
|
5
6
|
|
6
7
|
delegate :credential, :username, :challenge, :authenticatable_info, to: :context
|
7
8
|
|
8
9
|
def call
|
9
10
|
verify_credential!
|
10
|
-
store_passkey_and_register_agent!
|
11
11
|
|
12
|
+
agent.transaction do
|
13
|
+
store_passkey_and_register_agent!
|
14
|
+
create_authenticatable! if aux_class_name.present?
|
15
|
+
end
|
16
|
+
|
17
|
+
context.agent = agent
|
12
18
|
context.username = agent.username
|
13
19
|
context.auth_token = GenerateAuthToken.call!(agent:).auth_token
|
14
20
|
rescue Interactor::Failure => e
|
@@ -26,66 +32,16 @@ module PasskeysRails
|
|
26
32
|
end
|
27
33
|
|
28
34
|
def store_passkey_and_register_agent!
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
rescue StandardError => e
|
40
|
-
context.fail! code: :passkey_error, message: e.message
|
41
|
-
end
|
42
|
-
|
43
|
-
create_authenticatable! if aux_class_name.present?
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def authenticatable_class
|
48
|
-
authenticatable_info && authenticatable_info[:class]
|
49
|
-
end
|
50
|
-
|
51
|
-
def authenticatable_params
|
52
|
-
authenticatable_info && authenticatable_info[:params]
|
53
|
-
end
|
54
|
-
|
55
|
-
def aux_class_name
|
56
|
-
@aux_class_name ||= authenticatable_class || PasskeysRails.default_class
|
57
|
-
end
|
58
|
-
|
59
|
-
def aux_class
|
60
|
-
whitelist = PasskeysRails.class_whitelist
|
61
|
-
|
62
|
-
@aux_class ||= begin
|
63
|
-
if whitelist.is_a?(Array)
|
64
|
-
unless whitelist.include?(aux_class_name)
|
65
|
-
context.fail!(code: :invalid_authenticatable_class, message: "authenticatable_class (#{aux_class_name}) is not in the whitelist")
|
66
|
-
end
|
67
|
-
elsif whitelist.present?
|
68
|
-
context.fail!(code: :invalid_class_whitelist,
|
69
|
-
message: "class_whitelist is invalid. It should be nil or an array of zero or more class names.")
|
70
|
-
end
|
71
|
-
|
72
|
-
begin
|
73
|
-
aux_class_name.constantize
|
74
|
-
rescue StandardError
|
75
|
-
context.fail!(code: :invalid_authenticatable_class, message: "authenticatable_class (#{aux_class_name}) is not defined")
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def create_authenticatable!
|
81
|
-
authenticatable = aux_class.create! do |obj|
|
82
|
-
obj.agent = agent if obj.respond_to?(:agent=)
|
83
|
-
obj.registering_with(authenticatable_params) if obj.respond_to?(:registering_with)
|
84
|
-
end
|
85
|
-
|
86
|
-
agent.update!(authenticatable:)
|
87
|
-
rescue ActiveRecord::RecordInvalid => e
|
88
|
-
context.fail!(code: :record_invalid, message: e.message)
|
35
|
+
# Store Credential ID, Credential Public Key and Sign Count for future authentications
|
36
|
+
agent.passkeys.create!(
|
37
|
+
identifier: webauthn_credential.id,
|
38
|
+
public_key: webauthn_credential.public_key,
|
39
|
+
sign_count: webauthn_credential.sign_count
|
40
|
+
)
|
41
|
+
|
42
|
+
agent.update! registered_at: Time.current
|
43
|
+
rescue StandardError => e
|
44
|
+
context.fail! code: :passkey_error, message: e.message
|
89
45
|
end
|
90
46
|
|
91
47
|
def webauthn_credential
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module PasskeysRails
|
2
|
+
module AuthenticatableCreator
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
def create_authenticatable!
|
8
|
+
authenticatable = aux_class.new
|
9
|
+
authenticatable.agent = agent if authenticatable.respond_to?(:agent=)
|
10
|
+
authenticatable.registering_with(authenticatable_params) if authenticatable.respond_to?(:registering_with)
|
11
|
+
authenticatable.save!
|
12
|
+
|
13
|
+
agent.update!(authenticatable:)
|
14
|
+
rescue ActiveRecord::RecordInvalid => e
|
15
|
+
context.fail!(code: :record_invalid, message: e.message)
|
16
|
+
end
|
17
|
+
|
18
|
+
def aux_class_name
|
19
|
+
@aux_class_name ||= authenticatable_class || PasskeysRails.default_class
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def authenticatable_class
|
25
|
+
authenticatable_info && authenticatable_info[:class]
|
26
|
+
end
|
27
|
+
|
28
|
+
def authenticatable_params
|
29
|
+
authenticatable_info && authenticatable_info[:params]
|
30
|
+
end
|
31
|
+
|
32
|
+
def aux_class
|
33
|
+
whitelist = PasskeysRails.class_whitelist
|
34
|
+
|
35
|
+
@aux_class ||= begin
|
36
|
+
if whitelist.is_a?(Array)
|
37
|
+
unless whitelist.include?(aux_class_name)
|
38
|
+
context.fail!(code: :invalid_authenticatable_class, message: "authenticatable_class (#{aux_class_name}) is not in the whitelist")
|
39
|
+
end
|
40
|
+
elsif whitelist.present?
|
41
|
+
context.fail!(code: :invalid_class_whitelist,
|
42
|
+
message: "class_whitelist is invalid. It should be nil or an array of zero or more class names.")
|
43
|
+
end
|
44
|
+
|
45
|
+
begin
|
46
|
+
aux_class_name.constantize
|
47
|
+
rescue StandardError
|
48
|
+
context.fail!(code: :invalid_authenticatable_class, message: "authenticatable_class (#{aux_class_name}) is not defined")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module PasskeysRails
|
2
|
+
module Debuggable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
protected
|
6
|
+
|
7
|
+
def ensure_debug_mode
|
8
|
+
context.fail!(code: :not_allowed, message: 'Action not allowed') if username_regex.blank?
|
9
|
+
end
|
10
|
+
|
11
|
+
def ensure_regex_match
|
12
|
+
context.fail!(code: :not_allowed, message: 'Invalid username') unless username&.match?(username_regex)
|
13
|
+
end
|
14
|
+
|
15
|
+
def username_regex
|
16
|
+
PasskeysRails.debug_login_regex
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/config/routes.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
PasskeysRails::Engine.routes.draw do
|
2
|
-
post 'passkeys
|
3
|
-
post 'passkeys
|
4
|
-
post 'passkeys
|
5
|
-
post 'passkeys
|
2
|
+
post 'challenge', to: 'passkeys#challenge'
|
3
|
+
post 'register', to: 'passkeys#register'
|
4
|
+
post 'authenticate', to: 'passkeys#authenticate'
|
5
|
+
post 'refresh', to: 'passkeys#refresh'
|
6
6
|
|
7
|
-
#
|
7
|
+
# These routes exist to allow easier mobile app debugging as it may not
|
8
8
|
# be possible to acess Passkey functionality in mobile simulators.
|
9
9
|
# CAUTION: It is very insecure to set DEBUG_LOGIN_REGEX in a production environment.
|
10
10
|
constraints(->(_request) { PasskeysRails.debug_login_regex.present? }) do
|
11
|
-
post 'passkeys
|
11
|
+
post 'debug_login', to: 'passkeys#debug_login'
|
12
|
+
post 'debug_register', to: 'passkeys#debug_register'
|
12
13
|
end
|
13
14
|
end
|
@@ -45,4 +45,19 @@ PasskeysRails.config do |c|
|
|
45
45
|
# for example: %w[User AdminUser]
|
46
46
|
#
|
47
47
|
# c.class_whitelist = nil
|
48
|
+
|
49
|
+
# To subscribe to various events in PasskeysRails, use the subscribe method.
|
50
|
+
# It can be called multiple times to subscribe to more than one event.
|
51
|
+
#
|
52
|
+
# Valid events:
|
53
|
+
# :did_register
|
54
|
+
# :did_authenticate
|
55
|
+
# :did_refresh
|
56
|
+
#
|
57
|
+
# Each event will include the event name, current agent and http request.
|
58
|
+
#
|
59
|
+
# For example:
|
60
|
+
# c.subscribe(:did_register) do |event, agent, request|
|
61
|
+
# puts("#{event} | #{agent.id} | #{request.headers}")
|
62
|
+
# end
|
48
63
|
end
|
data/lib/passkeys-rails.rb
CHANGED
@@ -45,6 +45,26 @@ module PasskeysRails
|
|
45
45
|
# for example: %w[User AdminUser]
|
46
46
|
mattr_accessor :class_whitelist, default: nil
|
47
47
|
|
48
|
+
# Convenience method to subscribe to various events in PasskeysRails.
|
49
|
+
#
|
50
|
+
# Valid events:
|
51
|
+
# :did_register
|
52
|
+
# :did_authenticate
|
53
|
+
# :did_refresh
|
54
|
+
#
|
55
|
+
# Each event will include the event name, current agent and http request.
|
56
|
+
# For example:
|
57
|
+
#
|
58
|
+
# subscribe(:did_register) do |event, agent, request|
|
59
|
+
# # do something with the agent and/or request
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
def self.subscribe(event_name)
|
63
|
+
ActiveSupport::Notifications.subscribe("passkeys_rails.#{event_name}") do |name, _start, _finish, _id, payload|
|
64
|
+
yield(name.gsub(/^passkeys_rails\./, ''), payload[:agent], payload[:request]) if block_given?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
48
68
|
# This is only used by the debug_login endpoint.
|
49
69
|
# CAUTION: It is very insecure to set DEBUG_LOGIN_REGEX in a production environment.
|
50
70
|
def self.debug_login_regex
|
@@ -53,6 +73,8 @@ module PasskeysRails
|
|
53
73
|
|
54
74
|
# Returns an Interactor::Context that indicates if the request is authentic.
|
55
75
|
#
|
76
|
+
# `request` can be a String on an Http Request
|
77
|
+
#
|
56
78
|
# .success? is true if authentic
|
57
79
|
# .agent is the Passkey::Agent on success
|
58
80
|
#
|
@@ -60,10 +82,19 @@ module PasskeysRails
|
|
60
82
|
# .code is the error code on failure
|
61
83
|
# .message is the human readable error message on failure
|
62
84
|
def self.authenticate(request)
|
63
|
-
|
85
|
+
auth_token = if request.is_a?(String)
|
86
|
+
request
|
87
|
+
elsif request.respond_to?(:headers)
|
88
|
+
request.headers['X-Auth']
|
89
|
+
else
|
90
|
+
""
|
91
|
+
end
|
92
|
+
|
93
|
+
PasskeysRails::ValidateAuthToken.call(auth_token:)
|
64
94
|
end
|
65
95
|
|
66
96
|
# Raises a PasskeysRails::Error exception if the request is not authentic.
|
97
|
+
# `request` can be a String on an Http Request
|
67
98
|
def self.authenticate!(request)
|
68
99
|
auth = authenticate(request)
|
69
100
|
return if auth.success?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: passkeys-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Troy Anderson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -358,12 +358,15 @@ files:
|
|
358
358
|
- app/interactors/passkeys_rails/begin_challenge.rb
|
359
359
|
- app/interactors/passkeys_rails/begin_registration.rb
|
360
360
|
- app/interactors/passkeys_rails/debug_login.rb
|
361
|
+
- app/interactors/passkeys_rails/debug_register.rb
|
361
362
|
- app/interactors/passkeys_rails/finish_authentication.rb
|
362
363
|
- app/interactors/passkeys_rails/finish_registration.rb
|
363
364
|
- app/interactors/passkeys_rails/generate_auth_token.rb
|
364
365
|
- app/interactors/passkeys_rails/refresh_token.rb
|
365
366
|
- app/interactors/passkeys_rails/validate_auth_token.rb
|
366
367
|
- app/models/concerns/passkeys_rails/authenticatable.rb
|
368
|
+
- app/models/concerns/passkeys_rails/authenticatable_creator.rb
|
369
|
+
- app/models/concerns/passkeys_rails/debuggable.rb
|
367
370
|
- app/models/passkeys_rails/agent.rb
|
368
371
|
- app/models/passkeys_rails/application_record.rb
|
369
372
|
- app/models/passkeys_rails/error.rb
|