add_auth 0.2.1
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 +7 -0
- data/CHANGELOG.md +125 -0
- data/CODE_OF_CONDUCT.md +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +545 -0
- data/ROADMAP.md +244 -0
- data/SECURITY.md +75 -0
- data/app/controllers/add_auth/assets_controller.rb +54 -0
- data/app/controllers/add_auth/passkeys_controller.rb +144 -0
- data/app/controllers/add_auth/reauthentications_controller.rb +81 -0
- data/app/controllers/add_auth/recoveries_controller.rb +50 -0
- data/app/controllers/add_auth/sessions_controller.rb +62 -0
- data/app/controllers/add_auth/sign_ins_controller.rb +65 -0
- data/app/helpers/add_auth/sign_ins_helper.rb +42 -0
- data/app/javascript/controllers/.keep +0 -0
- data/app/jobs/add_auth/delivery_job.rb +31 -0
- data/app/jobs/add_auth/email_delivery_job.rb +15 -0
- data/app/jobs/add_auth/email_request_job.rb +21 -0
- data/app/jobs/add_auth/security_notification_job.rb +13 -0
- data/app/mailers/add_auth/security_mailer.rb +16 -0
- data/app/mailers/add_auth/sign_in_mailer.rb +36 -0
- data/app/models/concerns/.keep +0 -0
- data/app/views/.keep +0 -0
- data/app/views/add_auth/passkeys/_controls.html.erb +17 -0
- data/app/views/add_auth/passkeys/_list.html.erb +32 -0
- data/app/views/add_auth/reauthentications/_check_email.html.erb +3 -0
- data/app/views/add_auth/reauthentications/_confirmation.html.erb +7 -0
- data/app/views/add_auth/reauthentications/_different_browser.html.erb +3 -0
- data/app/views/add_auth/reauthentications/_form.html.erb +27 -0
- data/app/views/add_auth/reauthentications/_invalid_link.html.erb +3 -0
- data/app/views/add_auth/recoveries/_check_email.html.erb +3 -0
- data/app/views/add_auth/recoveries/_confirmation.html.erb +9 -0
- data/app/views/add_auth/recoveries/_form.html.erb +11 -0
- data/app/views/add_auth/recoveries/_invalid_link.html.erb +3 -0
- data/app/views/add_auth/security_mailer/notice.text.erb +3 -0
- data/app/views/add_auth/sessions/_list.html.erb +25 -0
- data/app/views/add_auth/sessions/_revoke_all.html.erb +18 -0
- data/app/views/add_auth/sessions/index.html.erb +12 -0
- data/app/views/add_auth/sign_in_mailer/link.text.erb +5 -0
- data/app/views/add_auth/sign_ins/_check_email.html.erb +5 -0
- data/app/views/add_auth/sign_ins/_confirmation.html.erb +9 -0
- data/app/views/add_auth/sign_ins/_different_browser.html.erb +3 -0
- data/app/views/add_auth/sign_ins/_form.html.erb +34 -0
- data/app/views/add_auth/sign_ins/_invalid_link.html.erb +3 -0
- data/app/views/add_auth/sign_ins/show.html.erb +1 -0
- data/app/views/layouts/add_auth/authentication.html.erb +19 -0
- data/lib/add_auth/configuration.rb +61 -0
- data/lib/add_auth/core/access_policy.rb +55 -0
- data/lib/add_auth/core/browser_binding.rb +28 -0
- data/lib/add_auth/core/challenge/base.rb +100 -0
- data/lib/add_auth/core/challenge/http.rb +130 -0
- data/lib/add_auth/core/challenge/null.rb +21 -0
- data/lib/add_auth/core/challenge/recaptcha.rb +76 -0
- data/lib/add_auth/core/challenge/test.rb +32 -0
- data/lib/add_auth/core/challenge/turnstile.rb +46 -0
- data/lib/add_auth/core/delivery.rb +55 -0
- data/lib/add_auth/core/digest/base.rb +34 -0
- data/lib/add_auth/core/digest/hmac.rb +41 -0
- data/lib/add_auth/core/intake.rb +59 -0
- data/lib/add_auth/core/maintenance.rb +45 -0
- data/lib/add_auth/core/rate_limit.rb +30 -0
- data/lib/add_auth/core/security_events.rb +48 -0
- data/lib/add_auth/core/sessions.rb +287 -0
- data/lib/add_auth/core/step_up.rb +129 -0
- data/lib/add_auth/core/strategies/email_link.rb +201 -0
- data/lib/add_auth/core/strategies/passkey.rb +286 -0
- data/lib/add_auth/rails/authentication.rb +67 -0
- data/lib/add_auth/rails/authentication_pages.rb +66 -0
- data/lib/add_auth/rails/delivery_cipher.rb +33 -0
- data/lib/add_auth/rails/doctor.rb +154 -0
- data/lib/add_auth/rails/ejection.rb +138 -0
- data/lib/add_auth/rails/elevation.rb +35 -0
- data/lib/add_auth/rails/engine.rb +34 -0
- data/lib/add_auth/rails/password_entry.rb +34 -0
- data/lib/add_auth/rails/runtime.rb +216 -0
- data/lib/add_auth/rails/stores/account_lock.rb +40 -0
- data/lib/add_auth/rails/stores/delivery_state.rb +21 -0
- data/lib/add_auth/rails/stores/email_tokens.rb +92 -0
- data/lib/add_auth/rails/stores/maintenance.rb +46 -0
- data/lib/add_auth/rails/stores/passkeys.rb +63 -0
- data/lib/add_auth/rails/stores/security_events.rb +40 -0
- data/lib/add_auth/rails/stores/sessions.rb +62 -0
- data/lib/add_auth/rails/user_lifecycle.rb +35 -0
- data/lib/add_auth/result.rb +55 -0
- data/lib/add_auth/testing.rb +30 -0
- data/lib/add_auth/version.rb +5 -0
- data/lib/add_auth.rb +52 -0
- data/lib/generators/add_auth/challenge/challenge_generator.rb +48 -0
- data/lib/generators/add_auth/challenge/templates/recaptcha.rb.tt +28 -0
- data/lib/generators/add_auth/challenge/templates/turnstile.rb.tt +24 -0
- data/lib/generators/add_auth/controllers/controllers_generator.rb +14 -0
- data/lib/generators/add_auth/ejection.rb +18 -0
- data/lib/generators/add_auth/email_link/email_link_generator.rb +56 -0
- data/lib/generators/add_auth/email_link/templates/add_add_auth_email_binding.rb.tt +5 -0
- data/lib/generators/add_auth/email_link/templates/add_add_auth_email_delivery.rb.tt +10 -0
- data/lib/generators/add_auth/email_link/templates/add_auth.css +28 -0
- data/lib/generators/add_auth/email_link/templates/add_auth_challenge.js +129 -0
- data/lib/generators/add_auth/email_tokens/email_tokens_generator.rb +35 -0
- data/lib/generators/add_auth/email_tokens/templates/add_auth_sign_in_token.rb +4 -0
- data/lib/generators/add_auth/email_tokens/templates/create_add_auth_sign_in_tokens.rb.tt +18 -0
- data/lib/generators/add_auth/install/install_generator.rb +27 -0
- data/lib/generators/add_auth/install/templates/initializer.rb +46 -0
- data/lib/generators/add_auth/javascript/javascript_generator.rb +14 -0
- data/lib/generators/add_auth/javascript/templates/application.js +2 -0
- data/lib/generators/add_auth/javascript/templates/codec.js +28 -0
- data/lib/generators/add_auth/javascript/templates/passkey.js +81 -0
- data/lib/generators/add_auth/mailer_views/mailer_views_generator.rb +14 -0
- data/lib/generators/add_auth/notifications/notifications_generator.rb +32 -0
- data/lib/generators/add_auth/notifications/templates/add_auth_security_event.rb +4 -0
- data/lib/generators/add_auth/notifications/templates/create_add_auth_security_events.rb.tt +18 -0
- data/lib/generators/add_auth/passkeys/passkeys_generator.rb +75 -0
- data/lib/generators/add_auth/passkeys/templates/add_add_auth_passkeys.rb.tt +45 -0
- data/lib/generators/add_auth/passkeys/templates/add_auth_ceremony.rb +4 -0
- data/lib/generators/add_auth/passkeys/templates/add_auth_credential.rb +4 -0
- data/lib/generators/add_auth/session_upgrade/session_upgrade_generator.rb +79 -0
- data/lib/generators/add_auth/session_upgrade/templates/add_add_auth_elevation.rb.tt +12 -0
- data/lib/generators/add_auth/session_upgrade/templates/extend_sessions_for_add_auth.rb.tt +14 -0
- data/lib/generators/add_auth/step_up/step_up_generator.rb +44 -0
- data/lib/generators/add_auth/step_up/templates/add_add_auth_reauthentication.rb.tt +9 -0
- data/lib/generators/add_auth/views/views_generator.rb +16 -0
- data/lib/tasks/add_auth.rake +38 -0
- metadata +361 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
class AddAddAuthPasskeys < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
# Rails 8.0 cannot reflect SQLite's FALSE literal emitted by Rails 8.1.
|
|
4
|
+
# Keep the additive schema readable by both supported Rails lines.
|
|
5
|
+
false_default = (connection.adapter_name == "SQLite") ? -> { "0" } : false
|
|
6
|
+
add_column :users, :webauthn_id, :string
|
|
7
|
+
add_index :users, :webauthn_id, unique: true
|
|
8
|
+
add_column :users, :add_auth_strict, :boolean, default: false_default, null: false
|
|
9
|
+
add_column :users, :add_auth_policy_version, :integer, default: 0, null: false
|
|
10
|
+
add_column :sessions, :authentication_policy_version, :integer, default: 0, null: false
|
|
11
|
+
add_column :sessions, :authentication_credential_id, :string
|
|
12
|
+
add_column :sessions, :authentication_uv, :boolean, default: false_default, null: false
|
|
13
|
+
create_table :add_auth_credentials do |t|
|
|
14
|
+
t.references :user, null: false, foreign_key: {on_delete: :cascade}
|
|
15
|
+
t.string :external_id, null: false
|
|
16
|
+
t.text :public_key, null: false
|
|
17
|
+
t.bigint :sign_count, default: 0, null: false
|
|
18
|
+
t.string :nickname, default: "Passkey", null: false, limit: 60
|
|
19
|
+
t.string :aaguid
|
|
20
|
+
t.json :transports
|
|
21
|
+
t.boolean :backup_eligible, default: false_default, null: false
|
|
22
|
+
t.boolean :backup_state, default: false_default, null: false
|
|
23
|
+
t.datetime :last_used_at
|
|
24
|
+
t.datetime :revoked_at
|
|
25
|
+
t.timestamps
|
|
26
|
+
end
|
|
27
|
+
add_index :add_auth_credentials, :external_id, unique: true
|
|
28
|
+
create_table :add_auth_ceremonies do |t|
|
|
29
|
+
t.string :digest, null: false
|
|
30
|
+
t.string :challenge, null: false
|
|
31
|
+
t.string :kind, null: false
|
|
32
|
+
t.string :browser_digest, null: false
|
|
33
|
+
t.string :configuration_digest, null: false
|
|
34
|
+
t.references :user, foreign_key: {on_delete: :cascade}
|
|
35
|
+
t.references :session, foreign_key: {on_delete: :cascade}
|
|
36
|
+
t.string :session_digest
|
|
37
|
+
t.string :authentication_purpose
|
|
38
|
+
t.datetime :expires_at, null: false
|
|
39
|
+
t.datetime :consumed_at
|
|
40
|
+
t.timestamps
|
|
41
|
+
end
|
|
42
|
+
add_index :add_auth_ceremonies, :digest, unique: true
|
|
43
|
+
add_index :add_auth_ceremonies, :expires_at
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module AddAuth
|
|
7
|
+
module Generators
|
|
8
|
+
class SessionUpgradeGenerator < ::Rails::Generators::Base
|
|
9
|
+
include ::Rails::Generators::Migration
|
|
10
|
+
|
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
|
12
|
+
|
|
13
|
+
def shared_browser_runtime
|
|
14
|
+
unless File.read(File.join(destination_root, "config/routes.rb")).include?("add_auth/application.js")
|
|
15
|
+
route 'get "add_auth/application.js", to: "add_auth/assets#application"'
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.next_migration_number(dirname) = ::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
|
20
|
+
|
|
21
|
+
def install_configuration
|
|
22
|
+
invoke "add_auth:install"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def migration
|
|
26
|
+
unless Dir[File.join(destination_root, "db/migrate/*_extend_sessions_for_add_auth.rb")].any?
|
|
27
|
+
migration_template "extend_sessions_for_add_auth.rb.tt", "db/migrate/extend_sessions_for_add_auth.rb"
|
|
28
|
+
end
|
|
29
|
+
unless Dir[File.join(destination_root, "db/migrate/*_add_add_auth_elevation.rb")].any?
|
|
30
|
+
migration_template "add_add_auth_elevation.rb.tt", "db/migrate/add_add_auth_elevation.rb"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def assets_and_sign_in
|
|
35
|
+
source = File.read(File.join(destination_root, "config/routes.rb"))
|
|
36
|
+
{"add_auth.css" => "assets#stylesheet", "add_auth/turbo.js" => "assets#turbo",
|
|
37
|
+
"add_auth/boot.js" => "assets#boot", "add_auth/stimulus.js" => "assets#stimulus", "add_auth/challenge.js" => "assets#challenge",
|
|
38
|
+
"sign-in" => "sign_ins#new"}.each do |path, action|
|
|
39
|
+
route %(get "#{path}", to: "add_auth/#{action}") unless source.include?(%("#{path}"))
|
|
40
|
+
end
|
|
41
|
+
route 'post "sign-in/password", to: "add_auth/sign_ins#password"' unless source.include?('"sign-in/password"')
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def hooks
|
|
45
|
+
path = "app/controllers/application_controller.rb"
|
|
46
|
+
source = File.read(File.join(destination_root, path))
|
|
47
|
+
unless source.include?("include Authentication")
|
|
48
|
+
raise Thor::Error, "Expected ApplicationController to include Authentication; integrate AddAuth::Rails::Authentication after it manually."
|
|
49
|
+
end
|
|
50
|
+
unless source.include?("include AddAuth::Rails::Authentication")
|
|
51
|
+
inject_into_file path, "\n include AddAuth::Rails::Authentication", after: "include Authentication"
|
|
52
|
+
end
|
|
53
|
+
path = "app/models/user.rb"
|
|
54
|
+
unless File.read(File.join(destination_root, path)).include?("include AddAuth::Rails::UserLifecycle")
|
|
55
|
+
inject_into_file path, " include AddAuth::Rails::UserLifecycle\n", after: /class User < [^\n]+\n/
|
|
56
|
+
end
|
|
57
|
+
path = "app/models/session.rb"
|
|
58
|
+
unless File.read(File.join(destination_root, path)).include?("self.filter_attributes")
|
|
59
|
+
inject_into_file path, " self.filter_attributes += [:token_digest, :elevation_credential_id]\n", after: /class Session < [^\n]+\n/
|
|
60
|
+
end
|
|
61
|
+
path = "config/initializers/add_auth.rb"
|
|
62
|
+
unless File.read(File.join(destination_root, path)).include?('require "add_auth/rails/authentication"')
|
|
63
|
+
prepend_to_file path, %(require "add_auth/rails/authentication"\nrequire "add_auth/rails/user_lifecycle"\n)
|
|
64
|
+
end
|
|
65
|
+
gsub_file path, "# config.session.enabled = true", "config.session.enabled = true"
|
|
66
|
+
routes = File.join(destination_root, "config/routes.rb")
|
|
67
|
+
source = File.read(routes)
|
|
68
|
+
unless source.include?("# AddAuth session management")
|
|
69
|
+
route <<~ROUTES
|
|
70
|
+
# AddAuth session management
|
|
71
|
+
get "sessions/revoke-all", to: "add_auth/sessions#new_revoke_all"
|
|
72
|
+
post "sessions/revoke-all", to: "add_auth/sessions#revoke_all"
|
|
73
|
+
resources :security_sessions, path: "sessions", only: [:index, :destroy], controller: "add_auth/sessions"
|
|
74
|
+
ROUTES
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class AddAddAuthElevation < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
# Rails 8.0 cannot reflect SQLite's FALSE literal emitted by Rails 8.1.
|
|
4
|
+
# Keep the additive schema readable by both supported Rails lines.
|
|
5
|
+
false_default = (connection.adapter_name == "SQLite") ? -> { "0" } : false
|
|
6
|
+
add_column :sessions, :elevated_at, :datetime
|
|
7
|
+
add_column :sessions, :elevated_with, :string
|
|
8
|
+
add_column :sessions, :elevation_purpose, :string
|
|
9
|
+
add_column :sessions, :elevation_credential_id, :string
|
|
10
|
+
add_column :sessions, :elevation_uv, :boolean, default: false_default, null: false
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class ExtendSessionsForAddAuth < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
add_column :sessions, :token_digest, :string, limit: 64
|
|
4
|
+
add_index :sessions, :token_digest, unique: true
|
|
5
|
+
add_column :sessions, :authenticated_with, :string
|
|
6
|
+
add_column :sessions, :authenticated_at, :datetime
|
|
7
|
+
add_column :sessions, :expires_at, :datetime
|
|
8
|
+
add_column :sessions, :last_seen_at, :datetime
|
|
9
|
+
add_column :sessions, :revoked_at, :datetime
|
|
10
|
+
add_index :sessions, [:user_id, :id], name: "index_add_auth_session_pages"
|
|
11
|
+
add_index :sessions, :expires_at
|
|
12
|
+
add_index :sessions, :revoked_at
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module AddAuth
|
|
7
|
+
module Generators
|
|
8
|
+
class StepUpGenerator < ::Rails::Generators::Base
|
|
9
|
+
include ::Rails::Generators::Migration
|
|
10
|
+
|
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
|
12
|
+
def self.next_migration_number(dirname) = ::ActiveRecord::Generators::Base.next_migration_number(dirname)
|
|
13
|
+
|
|
14
|
+
def dependencies
|
|
15
|
+
invoke "add_auth:email_link"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def persistence
|
|
19
|
+
unless Dir[File.join(destination_root, "db/migrate/*_add_add_auth_reauthentication.rb")].any?
|
|
20
|
+
migration_template "add_add_auth_reauthentication.rb.tt", "db/migrate/add_add_auth_reauthentication.rb"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def wiring
|
|
25
|
+
unless File.read(File.join(destination_root, "config/routes.rb")).include?("# AddAuth reauthentication")
|
|
26
|
+
route <<~ROUTES
|
|
27
|
+
# AddAuth reauthentication
|
|
28
|
+
get "reauthenticate", to: "add_auth/reauthentications#new"
|
|
29
|
+
post "reauthenticate/password", to: "add_auth/reauthentications#password"
|
|
30
|
+
post "reauthenticate/email", to: "add_auth/reauthentications#request_link"
|
|
31
|
+
get "reauthenticate/check-email", to: "add_auth/reauthentications#check_email"
|
|
32
|
+
get "reauthenticate/link", to: "add_auth/reauthentications#link"
|
|
33
|
+
post "reauthenticate/link", to: "add_auth/reauthentications#confirm"
|
|
34
|
+
ROUTES
|
|
35
|
+
end
|
|
36
|
+
initializer = "config/initializers/add_auth.rb"
|
|
37
|
+
unless File.read(File.join(destination_root, initializer)).include?("config.step_up.enabled = true")
|
|
38
|
+
append_to_file initializer, "\nAddAuth.configure do |config|\n config.step_up.enabled = true\n # Declare purpose methods and a fixed safe GET return_to before use.\nend\n"
|
|
39
|
+
end
|
|
40
|
+
say "Migrate and declare config.step_up.purposes. Use with_elevated_session around sensitive database mutations."
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
class AddAddAuthReauthentication < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
add_column :sessions, :elevation_version, :string
|
|
4
|
+
add_column :sessions, :elevation_expires_at, :datetime
|
|
5
|
+
add_reference :add_auth_sign_in_tokens, :session, foreign_key: {on_delete: :cascade}
|
|
6
|
+
add_column :add_auth_sign_in_tokens, :session_digest, :string
|
|
7
|
+
add_column :add_auth_sign_in_tokens, :authentication_purpose, :string
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "generators/add_auth/ejection"
|
|
5
|
+
|
|
6
|
+
module AddAuth
|
|
7
|
+
module Generators
|
|
8
|
+
class ViewsGenerator < ::Rails::Generators::Base
|
|
9
|
+
include Ejection
|
|
10
|
+
|
|
11
|
+
class_option :only, type: :string, default: "email_link"
|
|
12
|
+
|
|
13
|
+
def generate = eject(:views, only: options[:only])
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :add_auth do
|
|
4
|
+
desc "Check installed AddAuth authentication, delivery and maintenance wiring"
|
|
5
|
+
task doctor: :environment do
|
|
6
|
+
require "add_auth/rails/doctor"
|
|
7
|
+
doctor = AddAuth::Rails::Doctor.new
|
|
8
|
+
problems = doctor.call
|
|
9
|
+
Array(doctor.ejections).each do |entry|
|
|
10
|
+
puts "Customized: #{entry[:path]}" if entry[:customized]
|
|
11
|
+
puts entry[:diff] if entry[:diff]
|
|
12
|
+
end
|
|
13
|
+
puts problems.empty? ? "AddAuth configuration checks passed." : problems.join("\n")
|
|
14
|
+
abort "AddAuth configuration needs attention" if problems.any?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc "Run bounded delivery recovery and retention; schedule at least every minute"
|
|
18
|
+
task deliver_pending: :environment do
|
|
19
|
+
require "add_auth/rails/stores/maintenance"
|
|
20
|
+
models = {}
|
|
21
|
+
models[:session] = ::Session if AddAuth.configuration.session.enabled && defined?(::Session)
|
|
22
|
+
models[:ceremony] = ::AddAuthCeremony if defined?(::AddAuthCeremony)
|
|
23
|
+
models[:email] = ::AddAuthSignInToken if defined?(::AddAuthSignInToken)
|
|
24
|
+
models[:notification] = ::AddAuthSecurityEvent if defined?(::AddAuthSecurityEvent)
|
|
25
|
+
stores = models.filter_map do |kind, model|
|
|
26
|
+
[kind, AddAuth::Rails::Stores::Maintenance.new(model: model, kind: kind)] if model.table_exists?
|
|
27
|
+
end.to_h
|
|
28
|
+
jobs = {email: AddAuth::EmailDeliveryJob, notification: AddAuth::SecurityNotificationJob}
|
|
29
|
+
enqueue = lambda do |kind, id|
|
|
30
|
+
raise AddAuth::Error, "maintenance enqueue failed" unless jobs.fetch(kind).perform_later(id)
|
|
31
|
+
end
|
|
32
|
+
ActiveSupport::Notifications.instrument("maintenance.add_auth") do |payload|
|
|
33
|
+
payload.merge!(AddAuth::Core::Maintenance.new(stores: stores,
|
|
34
|
+
options: AddAuth.configuration.maintenance, enqueue: enqueue).call)
|
|
35
|
+
AddAuth::Rails::Runtime.record_maintenance
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: add_auth
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Taimoor Qureshi
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '8.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '8.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: webauthn
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '3.4'
|
|
33
|
+
- - ">="
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: 3.4.3
|
|
36
|
+
type: :runtime
|
|
37
|
+
prerelease: false
|
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - "~>"
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '3.4'
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: 3.4.3
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: public_suffix
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - "~>"
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '7.0'
|
|
53
|
+
type: :runtime
|
|
54
|
+
prerelease: false
|
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - "~>"
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '7.0'
|
|
60
|
+
- !ruby/object:Gem::Dependency
|
|
61
|
+
name: turbo-rails
|
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - "~>"
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '2.0'
|
|
67
|
+
type: :runtime
|
|
68
|
+
prerelease: false
|
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - "~>"
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '2.0'
|
|
74
|
+
- !ruby/object:Gem::Dependency
|
|
75
|
+
name: stimulus-rails
|
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - "~>"
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '1.3'
|
|
81
|
+
type: :runtime
|
|
82
|
+
prerelease: false
|
|
83
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - "~>"
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '1.3'
|
|
88
|
+
- !ruby/object:Gem::Dependency
|
|
89
|
+
name: bcrypt
|
|
90
|
+
requirement: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - "~>"
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: 3.1.7
|
|
95
|
+
type: :runtime
|
|
96
|
+
prerelease: false
|
|
97
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
98
|
+
requirements:
|
|
99
|
+
- - "~>"
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: 3.1.7
|
|
102
|
+
- !ruby/object:Gem::Dependency
|
|
103
|
+
name: rspec-rails
|
|
104
|
+
requirement: !ruby/object:Gem::Requirement
|
|
105
|
+
requirements:
|
|
106
|
+
- - "~>"
|
|
107
|
+
- !ruby/object:Gem::Version
|
|
108
|
+
version: '8.0'
|
|
109
|
+
type: :development
|
|
110
|
+
prerelease: false
|
|
111
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
112
|
+
requirements:
|
|
113
|
+
- - "~>"
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: '8.0'
|
|
116
|
+
- !ruby/object:Gem::Dependency
|
|
117
|
+
name: sqlite3
|
|
118
|
+
requirement: !ruby/object:Gem::Requirement
|
|
119
|
+
requirements:
|
|
120
|
+
- - ">="
|
|
121
|
+
- !ruby/object:Gem::Version
|
|
122
|
+
version: '2.1'
|
|
123
|
+
type: :development
|
|
124
|
+
prerelease: false
|
|
125
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
126
|
+
requirements:
|
|
127
|
+
- - ">="
|
|
128
|
+
- !ruby/object:Gem::Version
|
|
129
|
+
version: '2.1'
|
|
130
|
+
- !ruby/object:Gem::Dependency
|
|
131
|
+
name: pg
|
|
132
|
+
requirement: !ruby/object:Gem::Requirement
|
|
133
|
+
requirements:
|
|
134
|
+
- - "~>"
|
|
135
|
+
- !ruby/object:Gem::Version
|
|
136
|
+
version: '1.6'
|
|
137
|
+
type: :development
|
|
138
|
+
prerelease: false
|
|
139
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
140
|
+
requirements:
|
|
141
|
+
- - "~>"
|
|
142
|
+
- !ruby/object:Gem::Version
|
|
143
|
+
version: '1.6'
|
|
144
|
+
- !ruby/object:Gem::Dependency
|
|
145
|
+
name: standard
|
|
146
|
+
requirement: !ruby/object:Gem::Requirement
|
|
147
|
+
requirements:
|
|
148
|
+
- - "~>"
|
|
149
|
+
- !ruby/object:Gem::Version
|
|
150
|
+
version: '1.3'
|
|
151
|
+
type: :development
|
|
152
|
+
prerelease: false
|
|
153
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
154
|
+
requirements:
|
|
155
|
+
- - "~>"
|
|
156
|
+
- !ruby/object:Gem::Version
|
|
157
|
+
version: '1.3'
|
|
158
|
+
- !ruby/object:Gem::Dependency
|
|
159
|
+
name: capybara
|
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
|
161
|
+
requirements:
|
|
162
|
+
- - "~>"
|
|
163
|
+
- !ruby/object:Gem::Version
|
|
164
|
+
version: '3.40'
|
|
165
|
+
type: :development
|
|
166
|
+
prerelease: false
|
|
167
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
168
|
+
requirements:
|
|
169
|
+
- - "~>"
|
|
170
|
+
- !ruby/object:Gem::Version
|
|
171
|
+
version: '3.40'
|
|
172
|
+
- !ruby/object:Gem::Dependency
|
|
173
|
+
name: selenium-webdriver
|
|
174
|
+
requirement: !ruby/object:Gem::Requirement
|
|
175
|
+
requirements:
|
|
176
|
+
- - "~>"
|
|
177
|
+
- !ruby/object:Gem::Version
|
|
178
|
+
version: '4.48'
|
|
179
|
+
type: :development
|
|
180
|
+
prerelease: false
|
|
181
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
182
|
+
requirements:
|
|
183
|
+
- - "~>"
|
|
184
|
+
- !ruby/object:Gem::Version
|
|
185
|
+
version: '4.48'
|
|
186
|
+
- !ruby/object:Gem::Dependency
|
|
187
|
+
name: webmock
|
|
188
|
+
requirement: !ruby/object:Gem::Requirement
|
|
189
|
+
requirements:
|
|
190
|
+
- - "~>"
|
|
191
|
+
- !ruby/object:Gem::Version
|
|
192
|
+
version: '3.26'
|
|
193
|
+
type: :development
|
|
194
|
+
prerelease: false
|
|
195
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
196
|
+
requirements:
|
|
197
|
+
- - "~>"
|
|
198
|
+
- !ruby/object:Gem::Version
|
|
199
|
+
version: '3.26'
|
|
200
|
+
description: |
|
|
201
|
+
AddAuth extends the output of `bin/rails generate authentication` with the
|
|
202
|
+
credentials it does not cover: passkeys, email-link sign-in, purpose-bound
|
|
203
|
+
reauthentication and recovery. It adds hardened sessions, security mail and
|
|
204
|
+
challenge providers, with shared Turbo/HTML pages and permitted no-JavaScript
|
|
205
|
+
alternatives. It upgrades the generated User and Session models in place.
|
|
206
|
+
Documentation: https://addauthgem.com.
|
|
207
|
+
email:
|
|
208
|
+
- taimoorq@gmail.com
|
|
209
|
+
executables: []
|
|
210
|
+
extensions: []
|
|
211
|
+
extra_rdoc_files: []
|
|
212
|
+
files:
|
|
213
|
+
- CHANGELOG.md
|
|
214
|
+
- CODE_OF_CONDUCT.md
|
|
215
|
+
- LICENSE.txt
|
|
216
|
+
- README.md
|
|
217
|
+
- ROADMAP.md
|
|
218
|
+
- SECURITY.md
|
|
219
|
+
- app/controllers/add_auth/assets_controller.rb
|
|
220
|
+
- app/controllers/add_auth/passkeys_controller.rb
|
|
221
|
+
- app/controllers/add_auth/reauthentications_controller.rb
|
|
222
|
+
- app/controllers/add_auth/recoveries_controller.rb
|
|
223
|
+
- app/controllers/add_auth/sessions_controller.rb
|
|
224
|
+
- app/controllers/add_auth/sign_ins_controller.rb
|
|
225
|
+
- app/helpers/add_auth/sign_ins_helper.rb
|
|
226
|
+
- app/javascript/controllers/.keep
|
|
227
|
+
- app/jobs/add_auth/delivery_job.rb
|
|
228
|
+
- app/jobs/add_auth/email_delivery_job.rb
|
|
229
|
+
- app/jobs/add_auth/email_request_job.rb
|
|
230
|
+
- app/jobs/add_auth/security_notification_job.rb
|
|
231
|
+
- app/mailers/add_auth/security_mailer.rb
|
|
232
|
+
- app/mailers/add_auth/sign_in_mailer.rb
|
|
233
|
+
- app/models/concerns/.keep
|
|
234
|
+
- app/views/.keep
|
|
235
|
+
- app/views/add_auth/passkeys/_controls.html.erb
|
|
236
|
+
- app/views/add_auth/passkeys/_list.html.erb
|
|
237
|
+
- app/views/add_auth/reauthentications/_check_email.html.erb
|
|
238
|
+
- app/views/add_auth/reauthentications/_confirmation.html.erb
|
|
239
|
+
- app/views/add_auth/reauthentications/_different_browser.html.erb
|
|
240
|
+
- app/views/add_auth/reauthentications/_form.html.erb
|
|
241
|
+
- app/views/add_auth/reauthentications/_invalid_link.html.erb
|
|
242
|
+
- app/views/add_auth/recoveries/_check_email.html.erb
|
|
243
|
+
- app/views/add_auth/recoveries/_confirmation.html.erb
|
|
244
|
+
- app/views/add_auth/recoveries/_form.html.erb
|
|
245
|
+
- app/views/add_auth/recoveries/_invalid_link.html.erb
|
|
246
|
+
- app/views/add_auth/security_mailer/notice.text.erb
|
|
247
|
+
- app/views/add_auth/sessions/_list.html.erb
|
|
248
|
+
- app/views/add_auth/sessions/_revoke_all.html.erb
|
|
249
|
+
- app/views/add_auth/sessions/index.html.erb
|
|
250
|
+
- app/views/add_auth/sign_in_mailer/link.text.erb
|
|
251
|
+
- app/views/add_auth/sign_ins/_check_email.html.erb
|
|
252
|
+
- app/views/add_auth/sign_ins/_confirmation.html.erb
|
|
253
|
+
- app/views/add_auth/sign_ins/_different_browser.html.erb
|
|
254
|
+
- app/views/add_auth/sign_ins/_form.html.erb
|
|
255
|
+
- app/views/add_auth/sign_ins/_invalid_link.html.erb
|
|
256
|
+
- app/views/add_auth/sign_ins/show.html.erb
|
|
257
|
+
- app/views/layouts/add_auth/authentication.html.erb
|
|
258
|
+
- lib/add_auth.rb
|
|
259
|
+
- lib/add_auth/configuration.rb
|
|
260
|
+
- lib/add_auth/core/access_policy.rb
|
|
261
|
+
- lib/add_auth/core/browser_binding.rb
|
|
262
|
+
- lib/add_auth/core/challenge/base.rb
|
|
263
|
+
- lib/add_auth/core/challenge/http.rb
|
|
264
|
+
- lib/add_auth/core/challenge/null.rb
|
|
265
|
+
- lib/add_auth/core/challenge/recaptcha.rb
|
|
266
|
+
- lib/add_auth/core/challenge/test.rb
|
|
267
|
+
- lib/add_auth/core/challenge/turnstile.rb
|
|
268
|
+
- lib/add_auth/core/delivery.rb
|
|
269
|
+
- lib/add_auth/core/digest/base.rb
|
|
270
|
+
- lib/add_auth/core/digest/hmac.rb
|
|
271
|
+
- lib/add_auth/core/intake.rb
|
|
272
|
+
- lib/add_auth/core/maintenance.rb
|
|
273
|
+
- lib/add_auth/core/rate_limit.rb
|
|
274
|
+
- lib/add_auth/core/security_events.rb
|
|
275
|
+
- lib/add_auth/core/sessions.rb
|
|
276
|
+
- lib/add_auth/core/step_up.rb
|
|
277
|
+
- lib/add_auth/core/strategies/email_link.rb
|
|
278
|
+
- lib/add_auth/core/strategies/passkey.rb
|
|
279
|
+
- lib/add_auth/rails/authentication.rb
|
|
280
|
+
- lib/add_auth/rails/authentication_pages.rb
|
|
281
|
+
- lib/add_auth/rails/delivery_cipher.rb
|
|
282
|
+
- lib/add_auth/rails/doctor.rb
|
|
283
|
+
- lib/add_auth/rails/ejection.rb
|
|
284
|
+
- lib/add_auth/rails/elevation.rb
|
|
285
|
+
- lib/add_auth/rails/engine.rb
|
|
286
|
+
- lib/add_auth/rails/password_entry.rb
|
|
287
|
+
- lib/add_auth/rails/runtime.rb
|
|
288
|
+
- lib/add_auth/rails/stores/account_lock.rb
|
|
289
|
+
- lib/add_auth/rails/stores/delivery_state.rb
|
|
290
|
+
- lib/add_auth/rails/stores/email_tokens.rb
|
|
291
|
+
- lib/add_auth/rails/stores/maintenance.rb
|
|
292
|
+
- lib/add_auth/rails/stores/passkeys.rb
|
|
293
|
+
- lib/add_auth/rails/stores/security_events.rb
|
|
294
|
+
- lib/add_auth/rails/stores/sessions.rb
|
|
295
|
+
- lib/add_auth/rails/user_lifecycle.rb
|
|
296
|
+
- lib/add_auth/result.rb
|
|
297
|
+
- lib/add_auth/testing.rb
|
|
298
|
+
- lib/add_auth/version.rb
|
|
299
|
+
- lib/generators/add_auth/challenge/challenge_generator.rb
|
|
300
|
+
- lib/generators/add_auth/challenge/templates/recaptcha.rb.tt
|
|
301
|
+
- lib/generators/add_auth/challenge/templates/turnstile.rb.tt
|
|
302
|
+
- lib/generators/add_auth/controllers/controllers_generator.rb
|
|
303
|
+
- lib/generators/add_auth/ejection.rb
|
|
304
|
+
- lib/generators/add_auth/email_link/email_link_generator.rb
|
|
305
|
+
- lib/generators/add_auth/email_link/templates/add_add_auth_email_binding.rb.tt
|
|
306
|
+
- lib/generators/add_auth/email_link/templates/add_add_auth_email_delivery.rb.tt
|
|
307
|
+
- lib/generators/add_auth/email_link/templates/add_auth.css
|
|
308
|
+
- lib/generators/add_auth/email_link/templates/add_auth_challenge.js
|
|
309
|
+
- lib/generators/add_auth/email_tokens/email_tokens_generator.rb
|
|
310
|
+
- lib/generators/add_auth/email_tokens/templates/add_auth_sign_in_token.rb
|
|
311
|
+
- lib/generators/add_auth/email_tokens/templates/create_add_auth_sign_in_tokens.rb.tt
|
|
312
|
+
- lib/generators/add_auth/install/install_generator.rb
|
|
313
|
+
- lib/generators/add_auth/install/templates/initializer.rb
|
|
314
|
+
- lib/generators/add_auth/javascript/javascript_generator.rb
|
|
315
|
+
- lib/generators/add_auth/javascript/templates/application.js
|
|
316
|
+
- lib/generators/add_auth/javascript/templates/codec.js
|
|
317
|
+
- lib/generators/add_auth/javascript/templates/passkey.js
|
|
318
|
+
- lib/generators/add_auth/mailer_views/mailer_views_generator.rb
|
|
319
|
+
- lib/generators/add_auth/notifications/notifications_generator.rb
|
|
320
|
+
- lib/generators/add_auth/notifications/templates/add_auth_security_event.rb
|
|
321
|
+
- lib/generators/add_auth/notifications/templates/create_add_auth_security_events.rb.tt
|
|
322
|
+
- lib/generators/add_auth/passkeys/passkeys_generator.rb
|
|
323
|
+
- lib/generators/add_auth/passkeys/templates/add_add_auth_passkeys.rb.tt
|
|
324
|
+
- lib/generators/add_auth/passkeys/templates/add_auth_ceremony.rb
|
|
325
|
+
- lib/generators/add_auth/passkeys/templates/add_auth_credential.rb
|
|
326
|
+
- lib/generators/add_auth/session_upgrade/session_upgrade_generator.rb
|
|
327
|
+
- lib/generators/add_auth/session_upgrade/templates/add_add_auth_elevation.rb.tt
|
|
328
|
+
- lib/generators/add_auth/session_upgrade/templates/extend_sessions_for_add_auth.rb.tt
|
|
329
|
+
- lib/generators/add_auth/step_up/step_up_generator.rb
|
|
330
|
+
- lib/generators/add_auth/step_up/templates/add_add_auth_reauthentication.rb.tt
|
|
331
|
+
- lib/generators/add_auth/views/views_generator.rb
|
|
332
|
+
- lib/tasks/add_auth.rake
|
|
333
|
+
homepage: https://addauthgem.com
|
|
334
|
+
licenses:
|
|
335
|
+
- MIT
|
|
336
|
+
metadata:
|
|
337
|
+
homepage_uri: https://addauthgem.com
|
|
338
|
+
source_code_uri: https://github.com/taimoorq/add_auth/tree/master
|
|
339
|
+
changelog_uri: https://github.com/taimoorq/add_auth/blob/master/CHANGELOG.md
|
|
340
|
+
bug_tracker_uri: https://github.com/taimoorq/add_auth/issues
|
|
341
|
+
documentation_uri: https://addauthgem.com
|
|
342
|
+
rubygems_mfa_required: 'true'
|
|
343
|
+
allowed_push_host: https://rubygems.org
|
|
344
|
+
rdoc_options: []
|
|
345
|
+
require_paths:
|
|
346
|
+
- lib
|
|
347
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
348
|
+
requirements:
|
|
349
|
+
- - ">="
|
|
350
|
+
- !ruby/object:Gem::Version
|
|
351
|
+
version: 3.3.0
|
|
352
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
353
|
+
requirements:
|
|
354
|
+
- - ">="
|
|
355
|
+
- !ruby/object:Gem::Version
|
|
356
|
+
version: '0'
|
|
357
|
+
requirements: []
|
|
358
|
+
rubygems_version: 3.6.9
|
|
359
|
+
specification_version: 4
|
|
360
|
+
summary: Passkeys, email sign-in and session hardening for Rails 8 authentication.
|
|
361
|
+
test_files: []
|