passkeys-rails 0.1.2 → 0.1.4
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 +9 -0
- data/README.md +4 -4
- data/app/controllers/concerns/passkeys_rails/authentication.rb +29 -0
- data/app/controllers/passkeys_rails/application_controller.rb +22 -0
- data/app/controllers/passkeys_rails/passkeys_controller.rb +61 -0
- data/app/interactors/passkeys_rails/begin_authentication.rb +9 -0
- data/app/interactors/passkeys_rails/begin_challenge.rb +35 -0
- data/app/interactors/passkeys_rails/begin_registration.rb +23 -0
- data/app/interactors/passkeys_rails/finish_authentication.rb +53 -0
- data/app/interactors/passkeys_rails/finish_registration.rb +77 -0
- data/app/interactors/passkeys_rails/generate_auth_token.rb +27 -0
- data/app/interactors/passkeys_rails/refresh_token.rb +17 -0
- data/app/interactors/passkeys_rails/validate_auth_token.rb +33 -0
- data/app/models/concerns/passkeys_rails/authenticatable.rb +17 -0
- data/app/models/passkeys_rails/agent.rb +15 -0
- data/app/models/passkeys_rails/application_record.rb +5 -0
- data/app/models/passkeys_rails/error.rb +14 -0
- data/app/models/passkeys_rails/passkey.rb +8 -0
- data/config/initializers/application_controller.rb +12 -0
- data/config/routes.rb +1 -1
- data/lib/generators/passkeys_rails/USAGE +1 -1
- data/lib/generators/passkeys_rails/install_generator.rb +12 -14
- data/lib/generators/passkeys_rails/templates/README +1 -1
- data/lib/generators/passkeys_rails/templates/passkeys_rails_config.rb +1 -1
- data/lib/passkeys-rails.rb +21 -23
- data/lib/passkeys_rails/engine.rb +21 -0
- data/lib/passkeys_rails/railtie.rb +17 -0
- data/lib/passkeys_rails/version.rb +3 -0
- metadata +22 -24
- data/app/assets/config/passkeys_rails_manifest.js +0 -1
- data/app/assets/stylesheets/passkeys_rails/application.css +0 -15
- data/app/controllers/passkeys/rails/application_controller.rb +0 -24
- data/app/controllers/passkeys/rails/passkeys_controller.rb +0 -63
- data/app/models/concerns/passkeys/rails/authenticatable.rb +0 -19
- data/app/models/passkeys/rails/agent.rb +0 -17
- data/app/models/passkeys/rails/application_record.rb +0 -7
- data/app/models/passkeys/rails/error.rb +0 -16
- data/app/models/passkeys/rails/passkey.rb +0 -10
- data/app/views/layouts/passkeys/rails/application.html.erb +0 -15
- data/lib/passkeys/rails/controllers/helpers.rb +0 -33
- data/lib/passkeys/rails/engine.rb +0 -44
- data/lib/passkeys/rails/interactors/begin_authentication.rb +0 -11
- data/lib/passkeys/rails/interactors/begin_challenge.rb +0 -37
- data/lib/passkeys/rails/interactors/begin_registration.rb +0 -25
- data/lib/passkeys/rails/interactors/finish_authentication.rb +0 -55
- data/lib/passkeys/rails/interactors/finish_registration.rb +0 -79
- data/lib/passkeys/rails/interactors/generate_auth_token.rb +0 -29
- data/lib/passkeys/rails/interactors/refresh_token.rb +0 -19
- data/lib/passkeys/rails/interactors/validate_auth_token.rb +0 -35
- data/lib/passkeys/rails/railtie.rb +0 -19
- data/lib/passkeys/rails/version.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2eb3101d4ecba95639b4cc95adfa6a86939b7bdc3e6db0e3c26ed9e154d9fa97
|
4
|
+
data.tar.gz: 86cc744ced315cb5a662743f2cbf44fa20a05f7fbe444103b62a82281b009edd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2b55ac5a4aea2f969ab32cff126f6cab53f2a0b819a998668d58b794231a4f6ca378247af0e2561fbf0062760ea0724641c36b4915c2bd2a9f24aedf2909a23
|
7
|
+
data.tar.gz: 115f5c9c098da302f13d45e6871ae0152a8bd454d4d5281effc29afcc7e1dfacf0b838322bb55ff5f9b17caf2f846917e1596c0f8e4fc8a525bf710e986e7a5f
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
[](https://badge.fury.io/rb/passkeys-rails)
|
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
|
|
5
|
-
#
|
5
|
+
# PasskeysRails
|
6
6
|
Devise is awesome, but we don't need all that UI/UX for PassKeys. This gem is to make it easy to provide a back end that authenticates a mobile front end with PassKeys.
|
7
7
|
|
8
8
|
## Usage
|
9
9
|
rails passkeys-rails::install
|
10
|
-
|
10
|
+
PasskeysRails maintains an Agent model and related Passeys. If you have a user model, add `include PasskeysRails::Authenticatable` to your model and include the name of that class (e.g. "User") in the authenticatable_class param when calling the register API.
|
11
11
|
|
12
12
|
## Installation
|
13
13
|
Add this line to your application's Gemfile:
|
@@ -34,7 +34,7 @@ Depending on your application's configuration some manual setup may be required:
|
|
34
34
|
|
35
35
|
before_action :authenticate_passkey!, except: [:index]
|
36
36
|
|
37
|
-
2. Optionally include
|
37
|
+
2. Optionally include PasskeysRails::Authenticatable to the model(s) you are using as
|
38
38
|
your user model(s). For example, the User model.
|
39
39
|
|
40
40
|
3. See the reference mobile applications for how to use passkeys-rails for passkey
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module PasskeysRails
|
2
|
+
module Authentication
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
rescue_from PasskeysRails::Error do |e|
|
7
|
+
render json: e.to_h, status: :unauthorized
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def current_agent
|
12
|
+
return nil if request.headers['HTTP_X_AUTH'].blank?
|
13
|
+
|
14
|
+
@current_agent ||= validated_auth_token&.success? && validated_auth_token&.agent
|
15
|
+
end
|
16
|
+
|
17
|
+
def authenticate_passkey!
|
18
|
+
return if validated_auth_token.success?
|
19
|
+
|
20
|
+
raise PasskeysRails::Error.new(:authentication,
|
21
|
+
code: :unauthorized,
|
22
|
+
message: "You are not authorized to access this resource.")
|
23
|
+
end
|
24
|
+
|
25
|
+
def validated_auth_token
|
26
|
+
@validated_auth_token ||= PasskeysRails::ValidateAuthToken.call(auth_token: request.headers['HTTP_X_AUTH'])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module PasskeysRails
|
2
|
+
class ApplicationController < ActionController::Base
|
3
|
+
rescue_from ::Interactor::Failure, with: :handle_interactor_failure
|
4
|
+
rescue_from ActionController::ParameterMissing, with: :handle_missing_parameter
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
def handle_missing_parameter(error)
|
9
|
+
render_error(:authentication, 'missing_parameter', error.message)
|
10
|
+
end
|
11
|
+
|
12
|
+
def handle_interactor_failure(failure)
|
13
|
+
render_error(:authentication, failure.context.code, failure.context.message)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def render_error(context, code, message, status: :unprocessable_entity)
|
19
|
+
render json: { error: { context:, code:, message: } }, status:
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module PasskeysRails
|
2
|
+
class PasskeysController < ApplicationController
|
3
|
+
def challenge
|
4
|
+
result = PasskeysRails::BeginChallenge.call!(username: challenge_params[:username])
|
5
|
+
|
6
|
+
# Store the challenge so we can verify the future register or authentication request
|
7
|
+
session[:passkeys_rails] = result.session_data
|
8
|
+
|
9
|
+
render json: result.response.as_json
|
10
|
+
end
|
11
|
+
|
12
|
+
def register
|
13
|
+
result = PasskeysRails::FinishRegistration.call!(credential: attestation_credential_params.to_h,
|
14
|
+
authenticatable_class:,
|
15
|
+
username: session.dig(:passkeys_rails, :username),
|
16
|
+
challenge: session.dig(:passkeys_rails, :challenge))
|
17
|
+
|
18
|
+
render json: { username: result.username, auth_token: result.auth_token }
|
19
|
+
end
|
20
|
+
|
21
|
+
def authenticate
|
22
|
+
result = PasskeysRails::FinishAuthentication.call!(credential: authentication_params.to_h,
|
23
|
+
challenge: session.dig(:passkeys_rails, :challenge))
|
24
|
+
|
25
|
+
render json: { username: result.username, auth_token: result.auth_token }
|
26
|
+
end
|
27
|
+
|
28
|
+
def refresh
|
29
|
+
result = PasskeysRails::RefreshToken.call!(token: refresh_params[:auth_token])
|
30
|
+
render json: { username: result.username, auth_token: result.auth_token }
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def challenge_params
|
36
|
+
params.permit(:username)
|
37
|
+
end
|
38
|
+
|
39
|
+
def attestation_credential_params
|
40
|
+
credential = params.require(:credential)
|
41
|
+
credential.require(%i[id rawId type response])
|
42
|
+
credential.require(:response).require(%i[attestationObject clientDataJSON])
|
43
|
+
credential.permit(:id, :rawId, :type, { response: %i[attestationObject clientDataJSON] })
|
44
|
+
end
|
45
|
+
|
46
|
+
def authenticatable_class
|
47
|
+
params[:authenticatable_class]
|
48
|
+
end
|
49
|
+
|
50
|
+
def authentication_params
|
51
|
+
params.require(%i[id rawId type response])
|
52
|
+
params.require(:response).require(%i[authenticatorData clientDataJSON signature userHandle])
|
53
|
+
params.permit(:id, :rawId, :type, { response: %i[authenticatorData clientDataJSON signature userHandle] })
|
54
|
+
end
|
55
|
+
|
56
|
+
def refresh_params
|
57
|
+
params.require(:auth_token)
|
58
|
+
params.permit(:auth_token)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module PasskeysRails
|
2
|
+
class BeginChallenge
|
3
|
+
include Interactor
|
4
|
+
|
5
|
+
delegate :username, to: :context
|
6
|
+
|
7
|
+
def call
|
8
|
+
result = generate_challenge!
|
9
|
+
|
10
|
+
options = result.options
|
11
|
+
|
12
|
+
context.response = options
|
13
|
+
context.session_data = session_data(options)
|
14
|
+
rescue Interactor::Failure => e
|
15
|
+
context.fail! code: e.context.code, message: e.context.message
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def generate_challenge!
|
21
|
+
if username.present?
|
22
|
+
BeginRegistration.call!(username:)
|
23
|
+
else
|
24
|
+
BeginAuthentication.call!
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def session_data(options)
|
29
|
+
{
|
30
|
+
username:,
|
31
|
+
challenge: WebAuthn.standard_encoder.encode(options.challenge)
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module PasskeysRails
|
2
|
+
class BeginRegistration
|
3
|
+
include Interactor
|
4
|
+
|
5
|
+
delegate :username, to: :context
|
6
|
+
|
7
|
+
def call
|
8
|
+
agent = create_unregistered_agent
|
9
|
+
|
10
|
+
context.options = WebAuthn::Credential.options_for_create(user: { id: agent.webauthn_identifier, name: agent.username })
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def create_unregistered_agent
|
16
|
+
agent = Agent.create(username:, webauthn_identifier: WebAuthn.generate_user_id)
|
17
|
+
|
18
|
+
context.fail!(code: :validation_errors, message: agent.errors.full_messages.to_sentence) unless agent.valid?
|
19
|
+
|
20
|
+
agent
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Finish authentication ceremony
|
2
|
+
module PasskeysRails
|
3
|
+
class FinishAuthentication
|
4
|
+
include Interactor
|
5
|
+
|
6
|
+
delegate :credential, :challenge, to: :context
|
7
|
+
|
8
|
+
def call
|
9
|
+
verify_credential!
|
10
|
+
|
11
|
+
context.username = agent.username
|
12
|
+
context.auth_token = GenerateAuthToken.call!(agent:).auth_token
|
13
|
+
rescue Interactor::Failure => e
|
14
|
+
context.fail! code: e.context.code, message: e.context.message
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def verify_credential!
|
20
|
+
webauthn_credential.verify(
|
21
|
+
challenge,
|
22
|
+
public_key: passkey.public_key,
|
23
|
+
sign_count: passkey.sign_count
|
24
|
+
)
|
25
|
+
|
26
|
+
passkey.update!(sign_count: webauthn_credential.sign_count)
|
27
|
+
agent.update!(last_authenticated_at: Time.current)
|
28
|
+
rescue WebAuthn::SignCountVerificationError
|
29
|
+
# Cryptographic verification of the authenticator data succeeded, but the signature counter was less than or equal
|
30
|
+
# to the stored value. This can have several reasons and depending on your risk tolerance you can choose to fail or
|
31
|
+
# pass authentication. For more information see https://www.w3.org/TR/webauthn/#sign-counter
|
32
|
+
rescue WebAuthn::Error => e
|
33
|
+
context.fail!(code: :webauthn_error, message: e.message)
|
34
|
+
end
|
35
|
+
|
36
|
+
def webauthn_credential
|
37
|
+
@webauthn_credential ||= WebAuthn::Credential.from_get(credential)
|
38
|
+
end
|
39
|
+
|
40
|
+
def passkey
|
41
|
+
@passkey ||= begin
|
42
|
+
passkey = Passkey.find_by(identifier: webauthn_credential.id)
|
43
|
+
context.fail!(code: :passkey_not_found, message: "Unable to find the specified passkey") if passkey.blank?
|
44
|
+
|
45
|
+
passkey
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def agent
|
50
|
+
passkey.agent
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# Finish registration ceremony
|
2
|
+
module PasskeysRails
|
3
|
+
class FinishRegistration
|
4
|
+
include Interactor
|
5
|
+
|
6
|
+
delegate :credential, :username, :challenge, :authenticatable_class, to: :context
|
7
|
+
|
8
|
+
def call
|
9
|
+
verify_credential!
|
10
|
+
store_passkey_and_register_agent!
|
11
|
+
|
12
|
+
context.username = agent.username
|
13
|
+
context.auth_token = GenerateAuthToken.call!(agent:).auth_token
|
14
|
+
rescue Interactor::Failure => e
|
15
|
+
context.fail! code: e.context.code, message: e.context.message
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def verify_credential!
|
21
|
+
webauthn_credential.verify(challenge)
|
22
|
+
rescue WebAuthn::Error => e
|
23
|
+
context.fail!(code: :webauthn_error, message: e.message)
|
24
|
+
rescue StandardError => e
|
25
|
+
context.fail!(code: :error, message: e.message)
|
26
|
+
end
|
27
|
+
|
28
|
+
def store_passkey_and_register_agent!
|
29
|
+
agent.transaction do
|
30
|
+
begin
|
31
|
+
# Store Credential ID, Credential Public Key and Sign Count for future authentications
|
32
|
+
agent.passkeys.create!(
|
33
|
+
identifier: webauthn_credential.id,
|
34
|
+
public_key: webauthn_credential.public_key,
|
35
|
+
sign_count: webauthn_credential.sign_count
|
36
|
+
)
|
37
|
+
|
38
|
+
agent.update! registered_at: Time.current
|
39
|
+
rescue StandardError => e
|
40
|
+
context.fail! code: :passkey_error, message: e.message
|
41
|
+
end
|
42
|
+
|
43
|
+
create_authenticatable! if authenticatable_class.present?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_authenticatable!
|
48
|
+
klass = begin
|
49
|
+
authenticatable_class.constantize
|
50
|
+
rescue StandardError
|
51
|
+
context.fail!(code: :invalid_authenticatable_class, message: "authenticatable_class (#{authenticatable_class}) is not defined")
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
authenticatable = klass.create! do |obj|
|
56
|
+
obj.registering_with(agent) if obj.respond_to?(:registering_with)
|
57
|
+
end
|
58
|
+
agent.update!(authenticatable:)
|
59
|
+
rescue ActiveRecord::RecordInvalid => e
|
60
|
+
context.fail!(code: :record_invalid, message: e.message)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def webauthn_credential
|
65
|
+
@webauthn_credential ||= WebAuthn::Credential.from_create(credential)
|
66
|
+
end
|
67
|
+
|
68
|
+
def agent
|
69
|
+
@agent ||= begin
|
70
|
+
agent = Agent.find_by(username:)
|
71
|
+
context.fail!(code: :agent_not_found, message: "Agent not found for session value: \"#{username}\"") if agent.blank?
|
72
|
+
|
73
|
+
agent
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module PasskeysRails
|
2
|
+
class GenerateAuthToken
|
3
|
+
include Interactor
|
4
|
+
|
5
|
+
delegate :agent, to: :context
|
6
|
+
|
7
|
+
def call
|
8
|
+
context.auth_token = generate_auth_token
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def generate_auth_token
|
14
|
+
JWT.encode(jwt_payload,
|
15
|
+
PasskeysRails.auth_token_secret,
|
16
|
+
PasskeysRails.auth_token_algorithm)
|
17
|
+
end
|
18
|
+
|
19
|
+
def jwt_payload
|
20
|
+
expiration = (Time.current + PasskeysRails.auth_token_expires_in).to_i
|
21
|
+
|
22
|
+
payload = { agent_id: agent.id }
|
23
|
+
payload[:exp] = expiration unless expiration.zero?
|
24
|
+
payload
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Finish authentication ceremony
|
2
|
+
module PasskeysRails
|
3
|
+
class RefreshToken
|
4
|
+
include Interactor
|
5
|
+
|
6
|
+
delegate :token, to: :context
|
7
|
+
|
8
|
+
def call
|
9
|
+
agent = ValidateAuthToken.call!(auth_token: token).agent
|
10
|
+
|
11
|
+
context.username = agent.username
|
12
|
+
context.auth_token = GenerateAuthToken.call!(agent:).auth_token
|
13
|
+
rescue Interactor::Failure => e
|
14
|
+
context.fail! code: e.context.code, message: e.context.message
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module PasskeysRails
|
2
|
+
class ValidateAuthToken
|
3
|
+
include Interactor
|
4
|
+
|
5
|
+
delegate :auth_token, to: :context
|
6
|
+
|
7
|
+
def call
|
8
|
+
context.fail!(code: :missing_token, message: "X-Auth header is required") if auth_token.blank?
|
9
|
+
|
10
|
+
context.agent = fetch_agent
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def fetch_agent
|
16
|
+
agent = Agent.find_by(id: payload['agent_id'])
|
17
|
+
context.fail!(code: :invalid_token, message: "Invalid token - no agent exists with agent_id") if agent.blank?
|
18
|
+
|
19
|
+
agent
|
20
|
+
end
|
21
|
+
|
22
|
+
def payload
|
23
|
+
JWT.decode(auth_token,
|
24
|
+
PasskeysRails.auth_token_secret,
|
25
|
+
true,
|
26
|
+
{ required_claims: %w[exp agent_id], algorithm: PasskeysRails.auth_token_algorithm }).first
|
27
|
+
rescue JWT::ExpiredSignature
|
28
|
+
context.fail!(code: :expired_token, message: "The token has expired")
|
29
|
+
rescue StandardError => e
|
30
|
+
context.fail!(code: :token_error, message: e.message)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module PasskeysRails
|
4
|
+
module Authenticatable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
has_one :agent, as: :authenticatable
|
9
|
+
|
10
|
+
delegate :registered?, to: :agent, allow_nil: true
|
11
|
+
|
12
|
+
def registering_with(_agent)
|
13
|
+
# initialize required attributes
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module PasskeysRails
|
2
|
+
class Agent < ApplicationRecord
|
3
|
+
belongs_to :authenticatable, polymorphic: true, optional: true
|
4
|
+
has_many :passkeys
|
5
|
+
|
6
|
+
scope :registered, -> { where.not registered_at: nil }
|
7
|
+
scope :unregistered, -> { where registered_at: nil }
|
8
|
+
|
9
|
+
validates :username, presence: true, uniqueness: true
|
10
|
+
|
11
|
+
def registered?
|
12
|
+
registered_at.present?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# These should be autoloaded, but if these aren't required here, apps using this
|
2
|
+
# gem will throw an exception that PasskeysRails::Authentication can't be found
|
3
|
+
require_relative '../../app/controllers/concerns/passkeys_rails/authentication'
|
4
|
+
require_relative '../../app/models/passkeys_rails/error'
|
5
|
+
|
6
|
+
class ActionController::Base
|
7
|
+
include PasskeysRails::Authentication
|
8
|
+
end
|
9
|
+
|
10
|
+
class ActionController::API
|
11
|
+
include PasskeysRails::Authentication
|
12
|
+
end
|
data/config/routes.rb
CHANGED
@@ -1,22 +1,20 @@
|
|
1
1
|
require 'rails/generators'
|
2
2
|
|
3
|
-
module
|
4
|
-
module
|
5
|
-
|
6
|
-
|
7
|
-
source_root File.expand_path("templates", __dir__)
|
3
|
+
module PasskeysRails
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < ::Rails::Generators::Base
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
def copy_config
|
9
|
+
template 'passkeys_rails_config.rb', "config/initializers/passkeys_rails.rb"
|
10
|
+
end
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
def add_routes
|
13
|
+
route 'mount PasskeysRails::Engine => "/passkeys_rails"'
|
14
|
+
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
end
|
16
|
+
def show_readme
|
17
|
+
readme "README" if behavior == :invoke
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|
@@ -8,7 +8,7 @@ Depending on your application's configuration some manual setup may be required:
|
|
8
8
|
|
9
9
|
before_action :authenticate_passkey!, except: [:index]
|
10
10
|
|
11
|
-
2. Optionally include
|
11
|
+
2. Optionally include PasskeysRails::Authenticatable to the model(s) you are using as
|
12
12
|
your user model(s). For example, the User model.
|
13
13
|
|
14
14
|
3. See the reference mobile applications for how to use passkeys-rails for passkey
|
data/lib/passkeys-rails.rb
CHANGED
@@ -1,36 +1,34 @@
|
|
1
1
|
# rubocop:disable Naming/FileName
|
2
|
-
require '
|
3
|
-
require '
|
2
|
+
require 'passkeys_rails/engine'
|
3
|
+
require 'passkeys_rails/version'
|
4
4
|
require_relative "generators/passkeys_rails/install_generator"
|
5
5
|
|
6
|
-
module
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
mattr_accessor(:auth_token_secret)
|
6
|
+
module PasskeysRails
|
7
|
+
# Secret used to encode the auth token.
|
8
|
+
# Rails.application.secret_key_base is used if none is defined here.
|
9
|
+
# Changing this value will invalidate all tokens that have been fetched
|
10
|
+
# through the API.
|
11
|
+
mattr_accessor(:auth_token_secret)
|
13
12
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
# Algorithm used to generate the auth token.
|
14
|
+
# Changing this value will invalidate all tokens that have been fetched
|
15
|
+
# through the API.
|
16
|
+
mattr_accessor :auth_token_algorithm, default: "HS256"
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
# How long the auth token is valid before requiring a refresh or new login.
|
19
|
+
# Set it to 0 for no expiration (not recommended in production).
|
20
|
+
mattr_accessor :auth_token_expires_in, default: 30.days
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
22
|
+
class << self
|
23
|
+
def config
|
24
|
+
yield self
|
27
25
|
end
|
28
|
-
|
29
|
-
require 'passkeys/rails/railtie' if defined?(Rails)
|
30
26
|
end
|
27
|
+
|
28
|
+
require 'passkeys_rails/railtie' if defined?(Rails)
|
31
29
|
end
|
32
30
|
|
33
31
|
ActiveSupport.on_load(:before_initialize) do
|
34
|
-
|
32
|
+
PasskeysRails.auth_token_secret ||= Rails.application.secret_key_base
|
35
33
|
end
|
36
34
|
# rubocop:enable Naming/FileName
|