authentication-zero 2.9.2 → 2.11.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/.rubocop.yml +15 -0
- data/CHANGELOG.md +10 -0
- data/Gemfile.lock +1 -1
- data/README.md +8 -3
- data/authentication-zero-api.md +0 -3
- data/lib/authentication_zero/version.rb +1 -1
- data/lib/generators/authentication/authentication_generator.rb +47 -73
- data/lib/generators/authentication/templates/controllers/api/application_controller.rb.tt +27 -0
- data/lib/generators/authentication/templates/controllers/api/identity/emails_controller.rb.tt +3 -2
- data/lib/generators/authentication/templates/controllers/api/identity/password_resets_controller.rb.tt +4 -4
- data/lib/generators/authentication/templates/controllers/api/sessions/sudos_controller.rb.tt +1 -1
- data/lib/generators/authentication/templates/controllers/html/application_controller.rb.tt +25 -0
- data/lib/generators/authentication/templates/controllers/html/identity/emails_controller.rb.tt +3 -2
- data/lib/generators/authentication/templates/controllers/html/identity/password_resets_controller.rb.tt +4 -4
- data/lib/generators/authentication/templates/controllers/html/sessions/sudos_controller.rb.tt +4 -4
- data/lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt +13 -0
- data/lib/generators/authentication/templates/controllers/html/two_factor_authentication/challenges_controller.rb.tt +28 -0
- data/lib/generators/authentication/templates/controllers/html/two_factor_authentication/totps_controller.rb.tt +27 -0
- data/lib/generators/authentication/templates/erb/identity/emails/edit.html.erb.tt +5 -0
- data/lib/generators/authentication/templates/erb/identity/password_resets/edit.html.erb.tt +1 -1
- data/lib/generators/authentication/templates/erb/passwords/edit.html.erb.tt +2 -2
- data/lib/generators/authentication/templates/erb/sessions/sudos/new.html.erb.tt +2 -2
- data/lib/generators/authentication/templates/erb/two_factor_authentication/challenges/new.html.erb.tt +16 -0
- data/lib/generators/authentication/templates/erb/two_factor_authentication/totps/new.html.erb.tt +33 -0
- data/lib/generators/authentication/templates/migrations/create_sessions_migration.rb.tt +0 -3
- data/lib/generators/authentication/templates/migrations/create_table_migration.rb.tt +7 -4
- data/lib/generators/authentication/templates/models/model.rb.tt +8 -8
- data/lib/generators/authentication/templates/models/session.rb.tt +10 -3
- data/lib/generators/authentication/templates/test_unit/application_system_test_case.rb.tt +15 -0
- data/lib/generators/authentication/templates/test_unit/controllers/api/identity/email_verifications_controller_test.rb.tt +8 -8
- data/lib/generators/authentication/templates/test_unit/controllers/api/identity/emails_controller_test.rb.tt +9 -11
- data/lib/generators/authentication/templates/test_unit/controllers/api/identity/password_resets_controller_test.rb.tt +0 -3
- data/lib/generators/authentication/templates/test_unit/controllers/api/passwords_controller_test.rb.tt +6 -6
- data/lib/generators/authentication/templates/test_unit/controllers/api/sessions_controller_test.rb.tt +7 -7
- data/lib/generators/authentication/templates/test_unit/controllers/html/identity/email_verifications_controller_test.rb.tt +0 -4
- data/lib/generators/authentication/templates/test_unit/controllers/html/identity/emails_controller_test.rb.tt +5 -16
- data/lib/generators/authentication/templates/test_unit/controllers/html/identity/password_resets_controller_test.rb.tt +0 -3
- data/lib/generators/authentication/templates/test_unit/controllers/html/passwords_controller_test.rb.tt +0 -4
- data/lib/generators/authentication/templates/test_unit/controllers/html/sessions_controller_test.rb.tt +0 -4
- data/lib/generators/authentication/templates/test_unit/system/identity/emails_test.rb.tt +1 -10
- data/lib/generators/authentication/templates/test_unit/system/identity/password_resets_test.rb.tt +0 -3
- data/lib/generators/authentication/templates/test_unit/system/passwords_test.rb.tt +0 -10
- data/lib/generators/authentication/templates/test_unit/system/sessions_test.rb.tt +0 -10
- data/lib/generators/authentication/templates/test_unit/test_helper.rb.tt +22 -0
- metadata +11 -5
- data/lib/generators/authentication/templates/test_unit/controllers/api/sessions/sudos_controller_test.rb.tt +0 -24
- data/lib/generators/authentication/templates/test_unit/controllers/html/sessions/sudos_controller_test.rb.tt +0 -26
- data/lib/generators/authentication/templates/test_unit/system/sessions/sudos_test.rb.tt +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 702b78645aff0919daf1e518101731363068e6aef74fac0591f3257b5bf6b7a3
|
4
|
+
data.tar.gz: d07d22eb48277537484ef5f5c1cd4fd78f65e65d739fb7031384e3a594248e3a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99224479fcc817abaeed4492a5a48d071e98e3e32fcea32ec56e77007031b186feb0978405e5ec5f90750cf434bc86a554df73dea88245aa6185f18a16d7d2e3
|
7
|
+
data.tar.gz: 344af675d6c106d41c3a34dc2fdd04d93ca6028c83bc5acda497791a70a220a7854455cb336345d00d9371b529b7581d64617f4c244b1c15182bf5a869ada997
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
inherit_from: https://raw.githubusercontent.com/rails/rails/master/.rubocop.yml
|
2
|
+
|
3
|
+
Performance:
|
4
|
+
Exclude:
|
5
|
+
- 'test/**/*'
|
6
|
+
|
7
|
+
Style/FrozenStringLiteralComment:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Style/StringLiterals:
|
11
|
+
Enabled: true
|
12
|
+
EnforcedStyle: double_quotes
|
13
|
+
Include:
|
14
|
+
- 'app/**/*'
|
15
|
+
- 'test/**/*'
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## Authentication Zero 2.11.0 (March 27, 2022) ##
|
2
|
+
|
3
|
+
* Remove sudo from default generator
|
4
|
+
* Remove sudo_at from database
|
5
|
+
* Implement sudoable using redis
|
6
|
+
|
7
|
+
## Authentication Zero 2.10.0 (March 2, 2022) ##
|
8
|
+
|
9
|
+
* Implement two-factor
|
10
|
+
|
1
11
|
## Authentication Zero 2.9.0 (March 2, 2022) ##
|
2
12
|
|
3
13
|
* Implement trackable
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -11,8 +11,9 @@ The purpose of authentication zero is to generate a pre-built authentication sys
|
|
11
11
|
- Checks if a password has been found in any data breach (--pwned)
|
12
12
|
- Authentication by cookie
|
13
13
|
- Authentication by token (--api)
|
14
|
+
- Two factor authentication (--two-factor)
|
14
15
|
- Social Login with OmniAuth (--omniauthable)
|
15
|
-
- Ask password before sensitive data changes, aka: sudo
|
16
|
+
- Ask password before sensitive data changes, aka: sudo (--sudoable)
|
16
17
|
- Reset the user password and send reset instructions
|
17
18
|
- Reset the user password only from verified emails
|
18
19
|
- Lock sending reset password email after many attempts (--lockable)
|
@@ -53,7 +54,7 @@ root "home#index"
|
|
53
54
|
```
|
54
55
|
|
55
56
|
```
|
56
|
-
|
57
|
+
rails generate controller home index
|
57
58
|
```
|
58
59
|
|
59
60
|
Add these lines to your `app/views/home/index.html.erb`:
|
@@ -79,6 +80,10 @@ Add these lines to your `app/views/home/index.html.erb`:
|
|
79
80
|
<%# link_to "Activity Log", authentications_events_path %>
|
80
81
|
</div>
|
81
82
|
|
83
|
+
<div>
|
84
|
+
<%# link_to "Two-Factor Authentication", new_two_factor_authentication_totp_path %>
|
85
|
+
</div>
|
86
|
+
|
82
87
|
<br>
|
83
88
|
|
84
89
|
<%= button_to "Log out", Current.session, method: :delete %>
|
@@ -93,7 +98,7 @@ config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
|
|
93
98
|
## Usage
|
94
99
|
|
95
100
|
```
|
96
|
-
|
101
|
+
rails generate authentication user
|
97
102
|
```
|
98
103
|
|
99
104
|
Then run `bundle install` again!
|
data/authentication-zero-api.md
CHANGED
@@ -78,7 +78,6 @@ This endpoint will return `201 Created` with the current JSON representation of
|
|
78
78
|
"user_id": 1,
|
79
79
|
"user_agent": "insomnia/2022.1.0",
|
80
80
|
"ip_address": "127.0.0.1",
|
81
|
-
"sudo_at": "2022-03-04T17:20:33.632Z",
|
82
81
|
"created_at": "2022-03-04T17:20:33.632Z",
|
83
82
|
"updated_at": "2022-03-04T17:20:33.632Z"
|
84
83
|
},
|
@@ -87,7 +86,6 @@ This endpoint will return `201 Created` with the current JSON representation of
|
|
87
86
|
"user_id": 1,
|
88
87
|
"user_agent": "insomnia/2022.1.0",
|
89
88
|
"ip_address": "127.0.0.1",
|
90
|
-
"sudo_at": "2022-03-04T17:14:03.386Z",
|
91
89
|
"created_at": "2022-03-04T17:14:03.386Z",
|
92
90
|
"updated_at": "2022-03-04T17:14:03.386Z"
|
93
91
|
}
|
@@ -106,7 +104,6 @@ This endpoint will return `201 Created` with the current JSON representation of
|
|
106
104
|
"user_id": 1,
|
107
105
|
"user_agent": "insomnia/2022.1.0",
|
108
106
|
"ip_address": "127.0.0.1",
|
109
|
-
"sudo_at": "2022-03-04T17:14:03.386Z",
|
110
107
|
"created_at": "2022-03-04T17:14:03.386Z",
|
111
108
|
"updated_at": "2022-03-04T17:14:03.386Z"
|
112
109
|
}
|
@@ -5,17 +5,19 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
|
|
5
5
|
|
6
6
|
class_option :api, type: :boolean, desc: "Generates API authentication"
|
7
7
|
class_option :pwned, type: :boolean, desc: "Add pwned password validation"
|
8
|
+
class_option :sudoable, type: :boolean, desc: "Add password request before sensitive data changes"
|
8
9
|
class_option :lockable, type: :boolean, desc: "Add password reset locking"
|
9
10
|
class_option :ratelimit, type: :boolean, desc: "Add request rate limiting"
|
10
11
|
class_option :omniauthable, type: :boolean, desc: "Add social login support"
|
11
12
|
class_option :trackable, type: :boolean, desc: "Add activity log support"
|
13
|
+
class_option :two_factor, type: :boolean, desc: "Add two factor authentication"
|
12
14
|
|
13
15
|
source_root File.expand_path("templates", __dir__)
|
14
16
|
|
15
17
|
def add_gems
|
16
18
|
uncomment_lines "Gemfile", /"bcrypt"/
|
17
|
-
uncomment_lines "Gemfile", /"redis"/ if
|
18
|
-
uncomment_lines "Gemfile", /"kredis"/ if
|
19
|
+
uncomment_lines "Gemfile", /"redis"/ if redis?
|
20
|
+
uncomment_lines "Gemfile", /"kredis"/ if redis?
|
19
21
|
|
20
22
|
if options.pwned?
|
21
23
|
gem "pwned", comment: "Use Pwned to check if a password has been found in any of the huge data breaches [https://github.com/philnash/pwned]"
|
@@ -29,15 +31,20 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
|
|
29
31
|
gem "omniauth", comment: "Use OmniAuth to support multi-provider authentication [https://github.com/omniauth/omniauth]"
|
30
32
|
gem "omniauth-rails_csrf_protection", comment: "Provides a mitigation against CVE-2015-9284 [https://github.com/cookpad/omniauth-rails_csrf_protection]"
|
31
33
|
end
|
34
|
+
|
35
|
+
if two_factor?
|
36
|
+
gem "rotp", comment: "Use rotp for generating and validating one time passwords [https://github.com/mdp/rotp]"
|
37
|
+
gem "rqrcode", comment: "Use rqrcode for creating and rendering QR codes into various formats [https://github.com/whomwah/rqrcode]"
|
38
|
+
end
|
32
39
|
end
|
33
40
|
|
34
41
|
def create_configuration_files
|
35
|
-
|
36
|
-
|
42
|
+
copy_file "config/redis/shared.yml", "config/redis/shared.yml" if redis?
|
43
|
+
copy_file "config/initializers/omniauth.rb", "config/initializers/omniauth.rb" if omniauthable?
|
37
44
|
end
|
38
45
|
|
39
46
|
def add_environment_configurations
|
40
|
-
|
47
|
+
ratelimit_code = <<~CODE
|
41
48
|
# Rate limit general requests by IP address in a rate of 1000 requests per hour
|
42
49
|
config.middleware.use(Rack::Ratelimit, name: "General", rate: [1000, 1.hour], redis: Redis.new, logger: Rails.logger) { |env| ActionDispatch::Request.new(env).ip }
|
43
50
|
CODE
|
@@ -63,69 +70,15 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
|
|
63
70
|
template "test_unit/fixtures.yml", "test/fixtures/#{fixture_file_name}.yml"
|
64
71
|
end
|
65
72
|
|
66
|
-
def add_application_controller_methods
|
67
|
-
api_code = <<~CODE
|
68
|
-
include ActionController::HttpAuthentication::Token::ControllerMethods
|
69
|
-
|
70
|
-
before_action :set_current_request_details
|
71
|
-
before_action :authenticate
|
72
|
-
|
73
|
-
def require_sudo
|
74
|
-
if Current.session.sudo_at < 30.minutes.ago
|
75
|
-
render json: { error: "Enter your password to continue" }, status: :forbidden
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
private
|
80
|
-
def authenticate
|
81
|
-
if session = authenticate_with_http_token { |token, _| Session.find_signed(token) }
|
82
|
-
Current.session = session
|
83
|
-
else
|
84
|
-
request_http_token_authentication
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def set_current_request_details
|
89
|
-
Current.user_agent = request.user_agent
|
90
|
-
Current.ip_address = request.ip
|
91
|
-
end
|
92
|
-
CODE
|
93
|
-
|
94
|
-
html_code = <<~CODE
|
95
|
-
before_action :set_current_request_details
|
96
|
-
before_action :authenticate
|
97
|
-
|
98
|
-
def require_sudo
|
99
|
-
if Current.session.sudo_at < 30.minutes.ago
|
100
|
-
redirect_to new_sessions_sudo_path(proceed_to_url: request.url)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
private
|
105
|
-
def authenticate
|
106
|
-
if session = Session.find_by_id(cookies.signed[:session_token])
|
107
|
-
Current.session = session
|
108
|
-
else
|
109
|
-
redirect_to sign_in_path
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def set_current_request_details
|
114
|
-
Current.user_agent = request.user_agent
|
115
|
-
Current.ip_address = request.ip
|
116
|
-
end
|
117
|
-
CODE
|
118
|
-
|
119
|
-
inject_code = options.api? ? api_code : html_code
|
120
|
-
inject_into_class "app/controllers/application_controller.rb", "ApplicationController", optimize_indentation(inject_code, 2), verbose: false
|
121
|
-
end
|
122
|
-
|
123
73
|
def create_controllers
|
74
|
+
template "controllers/#{format_folder}/application_controller.rb", "app/controllers/application_controller.rb", force: true
|
75
|
+
|
124
76
|
directory "controllers/#{format_folder}/identity", "app/controllers/identity"
|
77
|
+
directory "controllers/#{format_folder}/two_factor_authentication", "app/controllers/two_factor_authentication" if two_factor?
|
78
|
+
template "controllers/#{format_folder}/sessions_controller.rb", "app/controllers/sessions_controller.rb"
|
125
79
|
template "controllers/#{format_folder}/passwords_controller.rb", "app/controllers/passwords_controller.rb"
|
126
80
|
template "controllers/#{format_folder}/registrations_controller.rb", "app/controllers/registrations_controller.rb"
|
127
|
-
template "controllers/#{format_folder}/
|
128
|
-
template "controllers/#{format_folder}/sessions/sudos_controller.rb", "app/controllers/sessions/sudos_controller.rb"
|
81
|
+
template "controllers/#{format_folder}/sessions/sudos_controller.rb", "app/controllers/sessions/sudos_controller.rb" if options.sudoable?
|
129
82
|
template "controllers/#{format_folder}/sessions/omniauth_controller.rb", "app/controllers/sessions/omniauth_controller.rb" if omniauthable?
|
130
83
|
template "controllers/#{format_folder}/authentications/events_controller.rb", "app/controllers/authentications/events_controller.rb" if options.trackable?
|
131
84
|
end
|
@@ -141,7 +94,13 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
|
|
141
94
|
directory "erb/identity", "app/views/identity"
|
142
95
|
directory "erb/passwords", "app/views/passwords"
|
143
96
|
directory "erb/registrations", "app/views/registrations"
|
144
|
-
|
97
|
+
|
98
|
+
template "erb/sessions/index.html.erb", "app/views/sessions/index.html.erb"
|
99
|
+
template "erb/sessions/new.html.erb", "app/views/sessions/new.html.erb"
|
100
|
+
|
101
|
+
directory "erb/sessions/sudos", "app/views/sessions/sudos" if options.sudoable?
|
102
|
+
|
103
|
+
directory "erb/two_factor_authentication", "app/views/two_factor_authentication" if two_factor?
|
145
104
|
directory "erb/authentications/events", "app/views/authentications/events" if options.trackable?
|
146
105
|
end
|
147
106
|
end
|
@@ -153,29 +112,36 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
|
|
153
112
|
def add_routes
|
154
113
|
if omniauthable?
|
155
114
|
route "post '/auth/:provider/callback', to: 'sessions/omniauth#create'"
|
156
|
-
route "get
|
157
|
-
route "get
|
115
|
+
route "get '/auth/:provider/callback', to: 'sessions/omniauth#create'"
|
116
|
+
route "get '/auth/failure', to: 'sessions/omniauth#failure'"
|
117
|
+
end
|
118
|
+
|
119
|
+
if two_factor?
|
120
|
+
route "resource :totp, only: [:new, :create]", namespace: :two_factor_authentication
|
121
|
+
route "resource :challenge, only: [:new, :create]", namespace: :two_factor_authentication
|
158
122
|
end
|
159
123
|
|
160
124
|
if options.trackable?
|
161
125
|
route "resources :events, only: :index", namespace: :authentications
|
162
126
|
end
|
163
127
|
|
164
|
-
route "resource :password_reset,
|
128
|
+
route "resource :password_reset, only: [:new, :edit, :create, :update]", namespace: :identity
|
165
129
|
route "resource :email_verification, only: [:edit, :create]", namespace: :identity
|
166
|
-
route "resource :email,
|
167
|
-
route "resource :sudo, only: [:new, :create]", namespace: :sessions
|
130
|
+
route "resource :email, only: [:edit, :update]", namespace: :identity
|
131
|
+
route "resource :sudo, only: [:new, :create]", namespace: :sessions if options.sudoable?
|
132
|
+
route "resource :password, only: [:edit, :update]"
|
168
133
|
route "resources :sessions, only: [:index, :show, :destroy]"
|
169
|
-
route "resource :password, only: [:edit, :update]"
|
170
134
|
route "post 'sign_up', to: 'registrations#create'"
|
171
|
-
route "get
|
135
|
+
route "get 'sign_up', to: 'registrations#new'" unless options.api?
|
172
136
|
route "post 'sign_in', to: 'sessions#create'"
|
173
|
-
route "get
|
137
|
+
route "get 'sign_in', to: 'sessions#new'" unless options.api?
|
174
138
|
end
|
175
139
|
|
176
140
|
def create_test_files
|
177
141
|
directory "test_unit/controllers/#{format_folder}", "test/controllers"
|
178
142
|
directory "test_unit/system", "test/system" unless options.api?
|
143
|
+
template "test_unit/test_helper.rb", "test/test_helper.rb", force: true
|
144
|
+
template "test_unit/application_system_test_case.rb", "test/application_system_test_case.rb", force: true unless options.api?
|
179
145
|
end
|
180
146
|
|
181
147
|
private
|
@@ -186,4 +152,12 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
|
|
186
152
|
def omniauthable?
|
187
153
|
options.omniauthable? && !options.api?
|
188
154
|
end
|
155
|
+
|
156
|
+
def two_factor?
|
157
|
+
options.two_factor? && !options.api?
|
158
|
+
end
|
159
|
+
|
160
|
+
def redis?
|
161
|
+
options.lockable? || options.sudoable?
|
162
|
+
end
|
189
163
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class ApplicationController < ActionController::API
|
2
|
+
include ActionController::HttpAuthentication::Token::ControllerMethods
|
3
|
+
|
4
|
+
before_action :set_current_request_details
|
5
|
+
before_action :authenticate
|
6
|
+
<%- if options.sudoable? %>
|
7
|
+
def require_sudo
|
8
|
+
unless Current.session.sudo?
|
9
|
+
render json: { error: "Enter your password to continue" }, status: :forbidden
|
10
|
+
end
|
11
|
+
end
|
12
|
+
<%- end -%>
|
13
|
+
|
14
|
+
private
|
15
|
+
def authenticate
|
16
|
+
if session = authenticate_with_http_token { |token, _| Session.find_signed(token) }
|
17
|
+
Current.session = session
|
18
|
+
else
|
19
|
+
request_http_token_authentication
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def set_current_request_details
|
24
|
+
Current.user_agent = request.user_agent
|
25
|
+
Current.ip_address = request.ip
|
26
|
+
end
|
27
|
+
end
|
data/lib/generators/authentication/templates/controllers/api/identity/emails_controller.rb.tt
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
class Identity::EmailsController < ApplicationController
|
2
|
-
before_action :require_sudo
|
3
2
|
before_action :set_<%= singular_table_name %>
|
4
3
|
|
5
4
|
def update
|
6
|
-
if
|
5
|
+
if !@<%= singular_table_name %>.authenticate(params[:current_password])
|
6
|
+
render json: { error: "The password you entered is incorrect" }, status: :bad_request
|
7
|
+
elsif @<%= singular_table_name %>.update(<%= "#{singular_table_name}_params" %>)
|
7
8
|
render json: @<%= singular_table_name %>
|
8
9
|
else
|
9
10
|
render json: @<%= singular_table_name %>.errors, status: :unprocessable_entity
|
@@ -1,9 +1,9 @@
|
|
1
1
|
class Identity::PasswordResetsController < ApplicationController
|
2
2
|
skip_before_action :authenticate
|
3
3
|
|
4
|
-
|
4
|
+
<%- if options.lockable? -%>
|
5
5
|
before_action :require_locking, only: :create
|
6
|
-
|
6
|
+
<%- end -%>
|
7
7
|
before_action :set_<%= singular_table_name %>, only: :update
|
8
8
|
|
9
9
|
def create
|
@@ -32,11 +32,11 @@ class Identity::PasswordResetsController < ApplicationController
|
|
32
32
|
def <%= "#{singular_table_name}_params" %>
|
33
33
|
params.permit(:password, :password_confirmation)
|
34
34
|
end
|
35
|
-
|
35
|
+
<%- if options.lockable? %>
|
36
36
|
def require_locking
|
37
37
|
Locking.lock_on("password_reset_lock:#{request.remote_ip}", wait: 1.hour, attempts: 10) do
|
38
38
|
render json: { error: "You've exceeded the maximum number of attempts" }, status: :too_many_requests
|
39
39
|
end
|
40
40
|
end
|
41
|
-
|
41
|
+
<%- end -%>
|
42
42
|
end
|
data/lib/generators/authentication/templates/controllers/api/sessions/sudos_controller.rb.tt
CHANGED
@@ -3,7 +3,7 @@ class Sessions::SudosController < ApplicationController
|
|
3
3
|
session = Current.session
|
4
4
|
|
5
5
|
if session.<%= singular_table_name %>.authenticate(params[:password])
|
6
|
-
session.
|
6
|
+
session.sudo.mark expires_in: 30.minutes
|
7
7
|
else
|
8
8
|
render json: { error: "The password you entered is incorrect" }, status: :bad_request
|
9
9
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class ApplicationController < ActionController::Base
|
2
|
+
before_action :set_current_request_details
|
3
|
+
before_action :authenticate
|
4
|
+
<%- if options.sudoable? %>
|
5
|
+
def require_sudo
|
6
|
+
unless Current.session.sudo?
|
7
|
+
redirect_to new_sessions_sudo_path(proceed_to_url: request.url)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
<%- end -%>
|
11
|
+
|
12
|
+
private
|
13
|
+
def authenticate
|
14
|
+
if session = Session.find_by_id(cookies.signed[:session_token])
|
15
|
+
Current.session = session
|
16
|
+
else
|
17
|
+
redirect_to sign_in_path
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_current_request_details
|
22
|
+
Current.user_agent = request.user_agent
|
23
|
+
Current.ip_address = request.ip
|
24
|
+
end
|
25
|
+
end
|
data/lib/generators/authentication/templates/controllers/html/identity/emails_controller.rb.tt
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
class Identity::EmailsController < ApplicationController
|
2
|
-
before_action :require_sudo
|
3
2
|
before_action :set_<%= singular_table_name %>
|
4
3
|
|
5
4
|
def edit
|
6
5
|
end
|
7
6
|
|
8
7
|
def update
|
9
|
-
if
|
8
|
+
if !@<%= singular_table_name %>.authenticate(params[:current_password])
|
9
|
+
redirect_to edit_identity_email_path, alert: "The password you entered is incorrect"
|
10
|
+
elsif @<%= singular_table_name %>.update(<%= "#{singular_table_name}_params" %>)
|
10
11
|
redirect_to root_path, notice: "Your email has been changed"
|
11
12
|
else
|
12
13
|
render :edit, status: :unprocessable_entity
|
@@ -1,9 +1,9 @@
|
|
1
1
|
class Identity::PasswordResetsController < ApplicationController
|
2
2
|
skip_before_action :authenticate
|
3
3
|
|
4
|
-
|
4
|
+
<%- if options.lockable? -%>
|
5
5
|
before_action :require_locking, only: :create
|
6
|
-
|
6
|
+
<%- end -%>
|
7
7
|
before_action :set_<%= singular_table_name %>, only: %i[ edit update ]
|
8
8
|
|
9
9
|
def new
|
@@ -39,11 +39,11 @@ class Identity::PasswordResetsController < ApplicationController
|
|
39
39
|
def <%= "#{singular_table_name}_params" %>
|
40
40
|
params.permit(:password, :password_confirmation)
|
41
41
|
end
|
42
|
-
|
42
|
+
<%- if options.lockable? %>
|
43
43
|
def require_locking
|
44
44
|
Locking.lock_on("password_reset_lock:#{request.remote_ip}", wait: 1.hour, attempts: 10) do
|
45
45
|
redirect_to new_identity_password_reset_path, alert: "You've exceeded the maximum number of attempts"
|
46
46
|
end
|
47
47
|
end
|
48
|
-
|
48
|
+
<%- end -%>
|
49
49
|
end
|
data/lib/generators/authentication/templates/controllers/html/sessions/sudos_controller.rb.tt
CHANGED
@@ -5,12 +5,12 @@ class Sessions::SudosController < ApplicationController
|
|
5
5
|
def create
|
6
6
|
session = Current.session
|
7
7
|
|
8
|
-
|
8
|
+
<%- if omniauthable? -%>
|
9
9
|
if session.<%= singular_table_name %>.authenticate(params[:password]) || session.<%= singular_table_name %>.provider
|
10
|
-
|
10
|
+
<%- else -%>
|
11
11
|
if session.<%= singular_table_name %>.authenticate(params[:password])
|
12
|
-
|
13
|
-
session.
|
12
|
+
<%- end -%>
|
13
|
+
session.sudo.mark(expires_in: 30.minutes); redirect_to(params[:proceed_to_url])
|
14
14
|
else
|
15
15
|
redirect_to new_sessions_sudo_path(proceed_to_url: params[:proceed_to_url]), alert: "The password you entered is incorrect"
|
16
16
|
end
|
@@ -15,10 +15,23 @@ class SessionsController < ApplicationController
|
|
15
15
|
<%= singular_table_name %> = <%= class_name %>.find_by(email: params[:email])
|
16
16
|
|
17
17
|
if <%= singular_table_name %> && <%= singular_table_name %>.authenticate(params[:password])
|
18
|
+
<%- if two_factor? -%>
|
19
|
+
if <%= singular_table_name %>.otp_secret
|
20
|
+
signed_id = <%= singular_table_name %>.signed_id(purpose: :authentication_challenge, expires_in: 20.minutes)
|
21
|
+
|
22
|
+
redirect_to new_two_factor_authentication_challenge_path(token: signed_id)
|
23
|
+
else
|
24
|
+
@session = <%= singular_table_name %>.sessions.create!
|
25
|
+
cookies.signed.permanent[:session_token] = { value: @session.id, httponly: true }
|
26
|
+
|
27
|
+
redirect_to root_path, notice: "Signed in successfully"
|
28
|
+
end
|
29
|
+
<%- else -%>
|
18
30
|
@session = <%= singular_table_name %>.sessions.create!
|
19
31
|
cookies.signed.permanent[:session_token] = { value: @session.id, httponly: true }
|
20
32
|
|
21
33
|
redirect_to root_path, notice: "Signed in successfully"
|
34
|
+
<%- end -%>
|
22
35
|
else
|
23
36
|
redirect_to sign_in_path(email_hint: params[:email]), alert: "That email or password is incorrect"
|
24
37
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class TwoFactorAuthentication::ChallengesController < ApplicationController
|
2
|
+
skip_before_action :authenticate
|
3
|
+
|
4
|
+
before_action :set_<%= singular_table_name %>
|
5
|
+
|
6
|
+
def new
|
7
|
+
end
|
8
|
+
|
9
|
+
def create
|
10
|
+
@totp = ROTP::TOTP.new(@<%= singular_table_name %>.otp_secret, issuer: "YourAppName")
|
11
|
+
|
12
|
+
if @totp.verify(params[:code], drift_behind: 15)
|
13
|
+
session = @<%= singular_table_name %>.sessions.create!
|
14
|
+
cookies.signed.permanent[:session_token] = { value: session.id, httponly: true }
|
15
|
+
|
16
|
+
redirect_to root_path, notice: "Signed in successfully"
|
17
|
+
else
|
18
|
+
redirect_to new_two_factor_authentication_challenge_path(token: params[:token]), alert: "That code didn't work. Please try again"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def set_<%= singular_table_name %>
|
24
|
+
@<%= singular_table_name %> = <%= class_name %>.find_signed!(params[:token], purpose: :authentication_challenge)
|
25
|
+
rescue
|
26
|
+
redirect_to sign_in_path, alert: "That's taking too long. Please re-enter your password and try again"
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class TwoFactorAuthentication::TotpsController < ApplicationController
|
2
|
+
before_action :set_<%= singular_table_name %>
|
3
|
+
before_action :set_totp
|
4
|
+
|
5
|
+
def new
|
6
|
+
@qr_code = RQRCode::QRCode.new(@totp.provisioning_uri(@<%= singular_table_name %>.email))
|
7
|
+
end
|
8
|
+
|
9
|
+
def create
|
10
|
+
if !@<%= singular_table_name %>.authenticate(params[:current_password])
|
11
|
+
redirect_to two_factor_authentication_totp_path, alert: "The password you entered is incorrect"
|
12
|
+
elsif @totp.verify(params[:code], drift_behind: 15)
|
13
|
+
@<%= singular_table_name %>.update! otp_secret: params[:secret]
|
14
|
+
redirect_to root_path, notice: "2FA is enabled on your account"
|
15
|
+
else
|
16
|
+
redirect_to two_factor_authentication_totp_path, alert: "That code didn't work. Please try again"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_<%= singular_table_name %>
|
21
|
+
@<%= singular_table_name %> = Current.<%= singular_table_name %>
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_totp
|
25
|
+
@totp = ROTP::TOTP.new(params[:secret] || ROTP::Base32.random, issuer: "YourAppName")
|
26
|
+
end
|
27
|
+
end
|
@@ -21,6 +21,11 @@
|
|
21
21
|
</div>
|
22
22
|
<%% end %>
|
23
23
|
|
24
|
+
<div>
|
25
|
+
<%%= form.label :current_password, style: "display: block" %>
|
26
|
+
<%%= form.password_field :current_password, required: true, autofocus: true, autocomplete: "current-password" %>
|
27
|
+
</div>
|
28
|
+
|
24
29
|
<div>
|
25
30
|
<%%= form.label :email, "New email", style: "display: block" %>
|
26
31
|
<%%= form.email_field :email %>
|
@@ -16,8 +16,8 @@
|
|
16
16
|
<%% end %>
|
17
17
|
|
18
18
|
<div>
|
19
|
-
<%%=
|
20
|
-
<%%=
|
19
|
+
<%%= form.label :current_password, style: "display: block" %>
|
20
|
+
<%%= form.password_field :current_password, required: true, autofocus: true, autocomplete: "current-password" %>
|
21
21
|
</div>
|
22
22
|
|
23
23
|
<div>
|
@@ -4,10 +4,10 @@
|
|
4
4
|
|
5
5
|
<%%= form_with(url: sessions_sudo_path) do |form| %>
|
6
6
|
|
7
|
-
<%%=
|
7
|
+
<%%= form.hidden_field :proceed_to_url, value: params[:proceed_to_url] %>
|
8
8
|
|
9
9
|
<div>
|
10
|
-
<%%=
|
10
|
+
<%%= form.password_field :password, required: true, autofocus: true, autocomplete: "current-password" %>
|
11
11
|
</div>
|
12
12
|
|
13
13
|
<div>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<p style="color: red"><%%= alert %></p>
|
2
|
+
|
3
|
+
<%%= form_with(url: two_factor_authentication_challenge_path) do |form| %>
|
4
|
+
<%%= form.hidden_field :token, value: params[:token] %>
|
5
|
+
|
6
|
+
<div>
|
7
|
+
<%%= form.label :code do %>
|
8
|
+
<h1>Next, open the 2FA authenticator app on your phone and type the six digit code below:</h1>
|
9
|
+
<%% end %>
|
10
|
+
<%%= form.text_field :code, autofocus: true, required: true, autocomplete: :off %>
|
11
|
+
</div>
|
12
|
+
|
13
|
+
<div>
|
14
|
+
<%%= form.submit "Verify" %>
|
15
|
+
</div>
|
16
|
+
<%% end %>
|