authentication-zero 2.10.0 → 2.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +15 -0
  3. data/CHANGELOG.md +6 -0
  4. data/Gemfile.lock +1 -1
  5. data/README.md +3 -1
  6. data/authentication-zero-api.md +0 -3
  7. data/lib/authentication_zero/version.rb +1 -1
  8. data/lib/generators/authentication/authentication_generator.rb +30 -20
  9. data/lib/generators/authentication/templates/controllers/api/application_controller.rb.tt +3 -2
  10. data/lib/generators/authentication/templates/controllers/api/identity/email_verifications_controller.rb.tt +9 -1
  11. data/lib/generators/authentication/templates/controllers/api/identity/emails_controller.rb.tt +3 -2
  12. data/lib/generators/authentication/templates/controllers/api/sessions/sudos_controller.rb.tt +1 -1
  13. data/lib/generators/authentication/templates/controllers/html/application_controller.rb.tt +3 -2
  14. data/lib/generators/authentication/templates/controllers/html/identity/emails_controller.rb.tt +3 -2
  15. data/lib/generators/authentication/templates/controllers/html/sessions/sudos_controller.rb.tt +1 -1
  16. data/lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt +13 -0
  17. data/lib/generators/authentication/templates/controllers/html/two_factor_authentication/totps_controller.rb.tt +3 -2
  18. data/lib/generators/authentication/templates/erb/identity/emails/edit.html.erb.tt +5 -0
  19. data/lib/generators/authentication/templates/erb/identity_mailer/email_verify_confirmation.html.erb.tt +5 -1
  20. data/lib/generators/authentication/templates/erb/identity_mailer/email_verify_confirmation.text.erb.tt +5 -1
  21. data/lib/generators/authentication/templates/erb/two_factor_authentication/totps/new.html.erb.tt +5 -0
  22. data/lib/generators/authentication/templates/mailers/identity_mailer.rb.tt +4 -0
  23. data/lib/generators/authentication/templates/migrations/create_sessions_migration.rb.tt +0 -3
  24. data/lib/generators/authentication/templates/models/model.rb.tt +3 -0
  25. data/lib/generators/authentication/templates/models/session.rb.tt +8 -1
  26. data/lib/generators/authentication/templates/test_unit/application_system_test_case.rb.tt +1 -1
  27. data/lib/generators/authentication/templates/test_unit/controllers/api/identity/email_verifications_controller_test.rb.tt +8 -4
  28. data/lib/generators/authentication/templates/test_unit/controllers/api/identity/emails_controller_test.rb.tt +9 -7
  29. data/lib/generators/authentication/templates/test_unit/controllers/api/identity/password_resets_controller_test.rb.tt +0 -3
  30. data/lib/generators/authentication/templates/test_unit/controllers/api/passwords_controller_test.rb.tt +6 -2
  31. data/lib/generators/authentication/templates/test_unit/controllers/api/sessions_controller_test.rb.tt +7 -3
  32. data/lib/generators/authentication/templates/test_unit/controllers/html/identity/emails_controller_test.rb.tt +5 -12
  33. data/lib/generators/authentication/templates/test_unit/controllers/html/identity/password_resets_controller_test.rb.tt +0 -3
  34. data/lib/generators/authentication/templates/test_unit/system/identity/emails_test.rb.tt +1 -0
  35. data/lib/generators/authentication/templates/test_unit/system/identity/password_resets_test.rb.tt +0 -3
  36. metadata +3 -6
  37. data/lib/generators/authentication/templates/controllers/html/sessions_controller_two_factor.rb.tt +0 -41
  38. data/lib/generators/authentication/templates/test_unit/controllers/api/sessions/sudos_controller_test.rb.tt +0 -20
  39. data/lib/generators/authentication/templates/test_unit/controllers/html/sessions/sudos_controller_test.rb.tt +0 -22
  40. data/lib/generators/authentication/templates/test_unit/system/sessions/sudos_test.rb.tt +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c0ad4048d0c9df83173acebd1bbf770f519a7bb2a8b5b45c7e9af3c268904052
4
- data.tar.gz: 06a897ebfc48122cfaf151a49b7412ef13b098069eb3bc7a2a4c088bcc711c90
3
+ metadata.gz: 650afdee62e14e099849af5cc58536c67b0cbdc0164e69d02a085c5e556cdfa1
4
+ data.tar.gz: 3435f8fc73fe7c7ff04a18b1dacd27b6d24c72d58ac2d98966d69228fd177a55
5
5
  SHA512:
6
- metadata.gz: fd26a42853d553616f366e1ddc57439ce91ba039f2cf25a5d3c80be48a6b0f0e7fee6816ead70587780ebc8a358e74e53543c0b6438eb39d7baf818aacc5191c
7
- data.tar.gz: 657bb45b7e9edfd6a036e9df9a2df41a2b2001199e65586d82548d7bd4ade28946be06ec3dbb28ba6fba100e275881c1a02dbb3de29b8546899393829b4cdc97
6
+ metadata.gz: cbec3a074fa00aa2f492c58b6940266de7e61502e7957e4f096d479a74f5a0663e5160e82289888fa7cff7b2138b3bd4472f6dd0e41ca48e4f5592b3dfd89e08
7
+ data.tar.gz: b2d51d9c1b6b562893d1a324c7904a9450dfa85858ecc3a6a18a8bf4f7230b3c8943e18b7f899312b0acd7e0b70f2464ce645144682d9cb73cbc9c62bc6f838a
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,9 @@
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
+
1
7
  ## Authentication Zero 2.10.0 (March 2, 2022) ##
2
8
 
3
9
  * Implement two-factor
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- authentication-zero (2.10.0)
4
+ authentication-zero (2.11.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -13,7 +13,9 @@ The purpose of authentication zero is to generate a pre-built authentication sys
13
13
  - Authentication by token (--api)
14
14
  - Two factor authentication (--two-factor)
15
15
  - Social Login with OmniAuth (--omniauthable)
16
- - Ask password before sensitive data changes, aka: sudo
16
+ - Verify email using a link with token
17
+ - Verify email using a six random digits code for api (--code-verifiable)
18
+ - Ask password before sensitive data changes, aka: sudo (--sudoable)
17
19
  - Reset the user password and send reset instructions
18
20
  - Reset the user password only from verified emails
19
21
  - Lock sending reset password email after many attempts (--lockable)
@@ -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
  }
@@ -1,3 +1,3 @@
1
1
  module AuthenticationZero
2
- VERSION = "2.10.0"
2
+ VERSION = "2.11.2"
3
3
  end
@@ -3,20 +3,22 @@ require "rails/generators/active_record"
3
3
  class AuthenticationGenerator < Rails::Generators::NamedBase
4
4
  include ActiveRecord::Generators::Migration
5
5
 
6
- class_option :api, type: :boolean, desc: "Generates API authentication"
7
- class_option :pwned, type: :boolean, desc: "Add pwned password validation"
8
- class_option :lockable, type: :boolean, desc: "Add password reset locking"
9
- class_option :ratelimit, type: :boolean, desc: "Add request rate limiting"
10
- class_option :omniauthable, type: :boolean, desc: "Add social login support"
11
- class_option :trackable, type: :boolean, desc: "Add activity log support"
12
- class_option :two_factor, type: :boolean, desc: "Add two factor authentication"
6
+ class_option :api, type: :boolean, desc: "Generates API authentication"
7
+ class_option :pwned, type: :boolean, desc: "Add pwned password validation"
8
+ class_option :code_verifiable, type: :boolean, desc: "Add email verification using a code for api"
9
+ class_option :sudoable, type: :boolean, desc: "Add password request before sensitive data changes"
10
+ class_option :lockable, type: :boolean, desc: "Add password reset locking"
11
+ class_option :ratelimit, type: :boolean, desc: "Add request rate limiting"
12
+ class_option :omniauthable, type: :boolean, desc: "Add social login support"
13
+ class_option :trackable, type: :boolean, desc: "Add activity log support"
14
+ class_option :two_factor, type: :boolean, desc: "Add two factor authentication"
13
15
 
14
16
  source_root File.expand_path("templates", __dir__)
15
17
 
16
18
  def add_gems
17
19
  uncomment_lines "Gemfile", /"bcrypt"/
18
- uncomment_lines "Gemfile", /"redis"/ if options.lockable?
19
- uncomment_lines "Gemfile", /"kredis"/ if options.lockable?
20
+ uncomment_lines "Gemfile", /"redis"/ if redis?
21
+ uncomment_lines "Gemfile", /"kredis"/ if redis?
20
22
 
21
23
  if options.pwned?
22
24
  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]"
@@ -38,7 +40,7 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
38
40
  end
39
41
 
40
42
  def create_configuration_files
41
- copy_file "config/redis/shared.yml", "config/redis/shared.yml" if options.lockable?
43
+ copy_file "config/redis/shared.yml", "config/redis/shared.yml" if redis?
42
44
  copy_file "config/initializers/omniauth.rb", "config/initializers/omniauth.rb" if omniauthable?
43
45
  end
44
46
 
@@ -72,17 +74,12 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
72
74
  def create_controllers
73
75
  template "controllers/#{format_folder}/application_controller.rb", "app/controllers/application_controller.rb", force: true
74
76
 
75
- if two_factor?
76
- directory "controllers/#{format_folder}/two_factor_authentication", "app/controllers/two_factor_authentication"
77
- template "controllers/#{format_folder}/sessions_controller_two_factor.rb", "app/controllers/sessions_controller.rb"
78
- else
79
- template "controllers/#{format_folder}/sessions_controller.rb", "app/controllers/sessions_controller.rb"
80
- end
81
-
82
77
  directory "controllers/#{format_folder}/identity", "app/controllers/identity"
78
+ directory "controllers/#{format_folder}/two_factor_authentication", "app/controllers/two_factor_authentication" if two_factor?
79
+ template "controllers/#{format_folder}/sessions_controller.rb", "app/controllers/sessions_controller.rb"
83
80
  template "controllers/#{format_folder}/passwords_controller.rb", "app/controllers/passwords_controller.rb"
84
81
  template "controllers/#{format_folder}/registrations_controller.rb", "app/controllers/registrations_controller.rb"
85
- template "controllers/#{format_folder}/sessions/sudos_controller.rb", "app/controllers/sessions/sudos_controller.rb"
82
+ template "controllers/#{format_folder}/sessions/sudos_controller.rb", "app/controllers/sessions/sudos_controller.rb" if options.sudoable?
86
83
  template "controllers/#{format_folder}/sessions/omniauth_controller.rb", "app/controllers/sessions/omniauth_controller.rb" if omniauthable?
87
84
  template "controllers/#{format_folder}/authentications/events_controller.rb", "app/controllers/authentications/events_controller.rb" if options.trackable?
88
85
  end
@@ -98,7 +95,12 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
98
95
  directory "erb/identity", "app/views/identity"
99
96
  directory "erb/passwords", "app/views/passwords"
100
97
  directory "erb/registrations", "app/views/registrations"
101
- directory "erb/sessions", "app/views/sessions"
98
+
99
+ template "erb/sessions/index.html.erb", "app/views/sessions/index.html.erb"
100
+ template "erb/sessions/new.html.erb", "app/views/sessions/new.html.erb"
101
+
102
+ directory "erb/sessions/sudos", "app/views/sessions/sudos" if options.sudoable?
103
+
102
104
  directory "erb/two_factor_authentication", "app/views/two_factor_authentication" if two_factor?
103
105
  directory "erb/authentications/events", "app/views/authentications/events" if options.trackable?
104
106
  end
@@ -127,7 +129,7 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
127
129
  route "resource :password_reset, only: [:new, :edit, :create, :update]", namespace: :identity
128
130
  route "resource :email_verification, only: [:edit, :create]", namespace: :identity
129
131
  route "resource :email, only: [:edit, :update]", namespace: :identity
130
- route "resource :sudo, only: [:new, :create]", namespace: :sessions
132
+ route "resource :sudo, only: [:new, :create]", namespace: :sessions if options.sudoable?
131
133
  route "resource :password, only: [:edit, :update]"
132
134
  route "resources :sessions, only: [:index, :show, :destroy]"
133
135
  route "post 'sign_up', to: 'registrations#create'"
@@ -155,4 +157,12 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
155
157
  def two_factor?
156
158
  options.two_factor? && !options.api?
157
159
  end
160
+
161
+ def code_verifiable?
162
+ options.code_verifiable? && options.api?
163
+ end
164
+
165
+ def redis?
166
+ options.lockable? || options.sudoable? || code_verifiable?
167
+ end
158
168
  end
@@ -3,12 +3,13 @@ class ApplicationController < ActionController::API
3
3
 
4
4
  before_action :set_current_request_details
5
5
  before_action :authenticate
6
-
6
+ <%- if options.sudoable? %>
7
7
  def require_sudo
8
- if Current.session.sudo_at < 30.minutes.ago
8
+ unless Current.session.sudo?
9
9
  render json: { error: "Enter your password to continue" }, status: :forbidden
10
10
  end
11
11
  end
12
+ <%- end -%>
12
13
 
13
14
  private
14
15
  def authenticate
@@ -13,8 +13,16 @@ class Identity::EmailVerificationsController < ApplicationController
13
13
 
14
14
  private
15
15
  def set_<%= singular_table_name %>
16
+ <%- if code_verifiable? -%>
17
+ @<%= singular_table_name %> = <%= class_name %>.find_by(email: params[:email])
18
+
19
+ unless @<%= singular_table_name %> && @<%= singular_table_name %>.verification_code.value == params[:token]
20
+ render json: { error: "That email verification code is invalid" }, status: :bad_request
21
+ end
22
+ <%- else -%>
16
23
  @<%= singular_table_name %> = <%= class_name %>.where(email: params[:email]).find_signed!(params[:token], purpose: params[:email])
17
24
  rescue
18
25
  render json: { error: "That email verification link is invalid" }, status: :bad_request
19
- end
26
+ <%- end -%>
27
+ end
20
28
  end
@@ -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 @<%= singular_table_name %>.update(<%= "#{singular_table_name}_params" %>)
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
@@ -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.update! sudo_at: Time.current
6
+ session.sudo.mark
7
7
  else
8
8
  render json: { error: "The password you entered is incorrect" }, status: :bad_request
9
9
  end
@@ -1,12 +1,13 @@
1
1
  class ApplicationController < ActionController::Base
2
2
  before_action :set_current_request_details
3
3
  before_action :authenticate
4
-
4
+ <%- if options.sudoable? %>
5
5
  def require_sudo
6
- if Current.session.sudo_at < 30.minutes.ago
6
+ unless Current.session.sudo?
7
7
  redirect_to new_sessions_sudo_path(proceed_to_url: request.url)
8
8
  end
9
9
  end
10
+ <%- end -%>
10
11
 
11
12
  private
12
13
  def authenticate
@@ -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 @<%= singular_table_name %>.update(<%= "#{singular_table_name}_params" %>)
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
@@ -10,7 +10,7 @@ class Sessions::SudosController < ApplicationController
10
10
  <%- else -%>
11
11
  if session.<%= singular_table_name %>.authenticate(params[:password])
12
12
  <%- end -%>
13
- session.update!(sudo_at: Time.current); redirect_to(params[:proceed_to_url])
13
+ session.sudo.mark; 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
@@ -1,5 +1,4 @@
1
1
  class TwoFactorAuthentication::TotpsController < ApplicationController
2
- before_action :require_sudo
3
2
  before_action :set_<%= singular_table_name %>
4
3
  before_action :set_totp
5
4
 
@@ -8,7 +7,9 @@ class TwoFactorAuthentication::TotpsController < ApplicationController
8
7
  end
9
8
 
10
9
  def create
11
- if @totp.verify(params[:code], drift_behind: 15)
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)
12
13
  @<%= singular_table_name %>.update! otp_secret: params[:secret]
13
14
  redirect_to root_path, notice: "2FA is enabled on your account"
14
15
  else
@@ -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 %>
@@ -2,9 +2,13 @@
2
2
 
3
3
  <p>This is to confirm that <%%= @<%= singular_table_name %>.email %> is the email you want to use on your account. If you ever lose your password, that's where we'll email a reset link.</p>
4
4
 
5
- <p><strong>You must hit the link below to confirm that you received this email.</strong></p>
5
+ <p><strong>You must <%= code_verifiable? ? "put the code" : "hit the link" %> below to confirm that you received this email.</strong></p>
6
6
 
7
+ <%- if code_verifiable? -%>
8
+ <strong><%%= @user.verification_code.value %></strong>
9
+ <%- else -%>
7
10
  <%%= link_to "Yes, use this email for my account", edit_identity_email_verification_url(token: @signed_id, email: @<%= singular_table_name %>.email) %>
11
+ <%- end -%>
8
12
 
9
13
  <hr>
10
14
 
@@ -2,8 +2,12 @@ Hey there,
2
2
 
3
3
  This is to confirm that <%%= @<%= singular_table_name %>.email %> is the email you want to use on your account. If you ever lose your password, that's where we'll email a reset link.
4
4
 
5
- You must hit the link below to confirm that you received this email.
5
+ You must <%= code_verifiable? ? "put the code" : "hit the link" %> below to confirm that you received this email.
6
6
 
7
+ <%- if code_verifiable? -%>
8
+ <%%= @user.verification_code.value %>
9
+ <%- else -%>
7
10
  [Yes, use this email for my account]<%%= edit_identity_email_verification_url(token: @signed_id, email: @<%= singular_table_name %>.email) %>
11
+ <%- end -%>
8
12
 
9
13
  Have questions or need help? Just reply to this email and our support team will help you sort it out.
@@ -17,6 +17,11 @@
17
17
  <%%= form_with(url: two_factor_authentication_totp_path) do |form| %>
18
18
  <%%= form.hidden_field :secret, value: @totp.secret %>
19
19
 
20
+ <div>
21
+ <%%= form.label :current_password, style: "display: block" %>
22
+ <%%= form.password_field :current_password, required: true, autofocus: true, autocomplete: "current-password" %>
23
+ </div>
24
+
20
25
  <div>
21
26
  <%%= form.label :code, "After scanning with your camera, the app will generate a six-digit code. Enter it here:", style: "display: block" %>
22
27
  <%%= form.text_field :code, autofocus: true, required: true, autocomplete: :off %>
@@ -8,7 +8,11 @@ class IdentityMailer < ApplicationMailer
8
8
 
9
9
  def email_verify_confirmation
10
10
  @<%= singular_table_name %> = params[:<%= singular_table_name %>]
11
+ <%- if code_verifiable? -%>
12
+ @<%= singular_table_name %>.verification_code.value = rand.to_s[2..7]
13
+ <%- else -%>
11
14
  @signed_id = @<%= singular_table_name %>.signed_id(purpose: @<%= singular_table_name %>.email, expires_in: 2.days)
15
+ <%- end -%>
12
16
 
13
17
  mail to: @<%= singular_table_name %>.email, subject: "Verify your email"
14
18
  end
@@ -2,12 +2,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
2
2
  def change
3
3
  create_table :sessions do |t|
4
4
  t.references :<%= singular_table_name %>, null: false, foreign_key: true
5
-
6
5
  t.string :user_agent
7
6
  t.string :ip_address
8
7
 
9
- t.datetime :sudo_at, null: false
10
-
11
8
  t.timestamps
12
9
  end
13
10
  end
@@ -5,6 +5,9 @@ class <%= class_name %> < ApplicationRecord
5
5
  <%- if options.trackable? -%>
6
6
  has_many :events, dependent: :destroy
7
7
  <%- end -%>
8
+ <%- if code_verifiable? %>
9
+ kredis_string :verification_code, expires_in: 2.days
10
+ <%- end -%>
8
11
 
9
12
  validates :email, presence: true, uniqueness: true
10
13
  validates_format_of :email, with: /\A[^@\s]+@[^@\s]+\z/
@@ -1,11 +1,18 @@
1
1
  class Session < ApplicationRecord
2
2
  belongs_to :<%= singular_table_name %>
3
+ <%- if options.sudoable? %>
4
+ kredis_flag :sudo, expires_in: 30.minutes
5
+ <%- end -%>
3
6
 
4
7
  before_create do
5
8
  self.user_agent = Current.user_agent
6
9
  self.ip_address = Current.ip_address
7
- self.sudo_at = Time.current
8
10
  end
11
+ <%- if options.sudoable? %>
12
+ after_create_commit do
13
+ self.sudo.mark
14
+ end
15
+ <%- end -%>
9
16
 
10
17
  after_create_commit do
11
18
  SessionMailer.with(session: self).signed_in_notification.deliver_later
@@ -10,6 +10,6 @@ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
10
10
  click_on "Sign in"
11
11
 
12
12
  assert_current_path root_url
13
- return <%= singular_table_name %>
13
+ <%= singular_table_name %>
14
14
  end
15
15
  end
@@ -9,21 +9,25 @@ class Identity::EmailVerificationsControllerTest < ActionDispatch::IntegrationTe
9
9
  @<%= singular_table_name %>.update! verified: false
10
10
  end
11
11
 
12
+ def default_headers
13
+ { "Authorization" => "Bearer #{@token}" }
14
+ end
15
+
12
16
  test "should send a verification email" do
13
17
  assert_enqueued_email_with IdentityMailer, :email_verify_confirmation, args: { <%= singular_table_name %>: @<%= singular_table_name %> } do
14
- post identity_email_verification_url, headers: { "Authorization" => "Bearer #{@token}" }
18
+ post identity_email_verification_url, headers: default_headers
15
19
  end
16
20
 
17
21
  assert_response :no_content
18
22
  end
19
23
 
20
24
  test "should verify email" do
21
- get edit_identity_email_verification_url, params: { token: @sid, email: @<%= singular_table_name %>.email }, headers: { "Authorization" => "Bearer #{@token}" }
25
+ get edit_identity_email_verification_url, params: { token: @sid, email: @<%= singular_table_name %>.email }, headers: default_headers
22
26
  assert_response :no_content
23
27
  end
24
28
 
25
29
  test "should not verify email with expired token" do
26
- get edit_identity_email_verification_url, params: { token: @sid_exp, email: @<%= singular_table_name %>.email }, headers: { "Authorization" => "Bearer #{@token}" }
30
+ get edit_identity_email_verification_url, params: { token: @sid_exp, email: @<%= singular_table_name %>.email }, headers: default_headers
27
31
 
28
32
  assert_response :bad_request
29
33
  assert_equal "That email verification link is invalid", response.parsed_body["error"]
@@ -32,7 +36,7 @@ class Identity::EmailVerificationsControllerTest < ActionDispatch::IntegrationTe
32
36
  test "should not verify email with previous token" do
33
37
  @<%= singular_table_name %>.update! email: "other_email@hey.com"
34
38
 
35
- get edit_identity_email_verification_url, params: { token: @sid, email: @<%= singular_table_name %>.email_previously_was }, headers: { "Authorization" => "Bearer #{@token}" }
39
+ get edit_identity_email_verification_url, params: { token: @sid, email: @<%= singular_table_name %>.email_previously_was }, headers: default_headers
36
40
 
37
41
  assert_response :bad_request
38
42
  assert_equal "That email verification link is invalid", response.parsed_body["error"]
@@ -5,17 +5,19 @@ class Identity::EmailsControllerTest < ActionDispatch::IntegrationTest
5
5
  @<%= singular_table_name %>, @token = sign_in_as(<%= table_name %>(:lazaro_nixon))
6
6
  end
7
7
 
8
+ def default_headers
9
+ { "Authorization" => "Bearer #{@token}" }
10
+ end
11
+
8
12
  test "should update email" do
9
- patch identity_email_url, params: { email: "new_email@hey.com" }, headers: { "Authorization" => "Bearer #{@token}" }
13
+ patch identity_email_url, params: { email: "new_email@hey.com", current_password: "Secret1*3*5*" }, headers: default_headers
10
14
  assert_response :success
11
15
  end
12
16
 
13
- test "should not update email without sudo" do
14
- @<%= singular_table_name %>.sessions.last.update! sudo_at: 1.day.ago
15
-
16
- patch identity_email_url, params: { email: "new_email@hey.com" }, headers: { "Authorization" => "Bearer #{@token}" }
17
+ test "should not update email with wrong current password" do
18
+ patch identity_email_url, params: { email: "new_email@hey.com", current_password: "SecretWrong1*3" }, headers: default_headers
17
19
 
18
- assert_response :forbidden
19
- assert_equal "Enter your password to continue", response.parsed_body["error"]
20
+ assert_response :bad_request
21
+ assert_equal "The password you entered is incorrect", response.parsed_body["error"]
20
22
  end
21
23
  end
@@ -6,9 +6,6 @@ class Identity::PasswordResetsControllerTest < ActionDispatch::IntegrationTest
6
6
  @sid = @<%= singular_table_name %>.signed_id(purpose: :password_reset, expires_in: 20.minutes)
7
7
  @sid_exp = @<%= singular_table_name %>.signed_id(purpose: :password_reset, expires_in: 0.minutes)
8
8
  end
9
- <%- if options.lockable? %>
10
- teardown { Kredis.clear_all }
11
- <%- end -%>
12
9
 
13
10
  test "should send a password reset email" do
14
11
  assert_enqueued_email_with IdentityMailer, :password_reset_provision, args: { <%= singular_table_name %>: @<%= singular_table_name %> } do
@@ -5,13 +5,17 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
5
5
  @<%= singular_table_name %>, @token = sign_in_as(<%= table_name %>(:lazaro_nixon))
6
6
  end
7
7
 
8
+ def default_headers
9
+ { "Authorization" => "Bearer #{@token}" }
10
+ end
11
+
8
12
  test "should update password" do
9
- patch password_url, params: { current_password: "Secret1*3*5*", password: "Secret6*4*2*", password_confirmation: "Secret6*4*2*" }, headers: { "Authorization" => "Bearer #{@token}" }
13
+ patch password_url, params: { current_password: "Secret1*3*5*", password: "Secret6*4*2*", password_confirmation: "Secret6*4*2*" }, headers: default_headers
10
14
  assert_response :success
11
15
  end
12
16
 
13
17
  test "should not update password with wrong current password" do
14
- patch password_url, params: { current_password: "SecretWrong1*3", password: "Secret6*4*2*", password_confirmation: "Secret6*4*2*" }, headers: { "Authorization" => "Bearer #{@token}" }
18
+ patch password_url, params: { current_password: "SecretWrong1*3", password: "Secret6*4*2*", password_confirmation: "Secret6*4*2*" }, headers: default_headers
15
19
 
16
20
  assert_response :bad_request
17
21
  assert_equal "The current password you entered is incorrect", response.parsed_body["error"]
@@ -5,13 +5,17 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
5
5
  @<%= singular_table_name %>, @token = sign_in_as(<%= table_name %>(:lazaro_nixon))
6
6
  end
7
7
 
8
+ def default_headers
9
+ { "Authorization" => "Bearer #{@token}" }
10
+ end
11
+
8
12
  test "should get index" do
9
- get sessions_url, headers: { "Authorization" => "Bearer #{@token}" }
13
+ get sessions_url, headers: default_headers
10
14
  assert_response :success
11
15
  end
12
16
 
13
17
  test "should show session" do
14
- get session_url(@<%= singular_table_name %>.sessions.last), headers: { "Authorization" => "Bearer #{@token}" }
18
+ get session_url(@<%= singular_table_name %>.sessions.last), headers: default_headers
15
19
  assert_response :success
16
20
  end
17
21
 
@@ -28,7 +32,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
28
32
  end
29
33
 
30
34
  test "should sign out" do
31
- delete session_url(@<%= singular_table_name %>.sessions.last), headers: { "Authorization" => "Bearer #{@token}" }
35
+ delete session_url(@<%= singular_table_name %>.sessions.last), headers: default_headers
32
36
  assert_response :no_content
33
37
  end
34
38
  end
@@ -10,22 +10,15 @@ class Identity::EmailsControllerTest < ActionDispatch::IntegrationTest
10
10
  assert_response :success
11
11
  end
12
12
 
13
- test "should not get edit without sudo" do
14
- @<%= singular_table_name %>.sessions.last.update! sudo_at: 1.day.ago
15
-
16
- get edit_identity_email_url
17
- assert_redirected_to new_sessions_sudo_url(proceed_to_url: edit_identity_email_url)
18
- end
19
-
20
13
  test "should update email" do
21
- patch identity_email_url, params: { email: "new_email@hey.com" }
14
+ patch identity_email_url, params: { email: "new_email@hey.com", current_password: "Secret1*3*5*" }
22
15
  assert_redirected_to root_url
23
16
  end
24
17
 
25
- test "should not update email without sudo" do
26
- @<%= singular_table_name %>.sessions.last.update! sudo_at: 1.day.ago
18
+ test "should not update email with wrong current password" do
19
+ patch identity_email_url, params: { email: "new_email@hey.com", current_password: "SecretWrong1*3" }
27
20
 
28
- patch identity_email_url, params: { email: "new_email@hey.com" }
29
- assert_redirected_to new_sessions_sudo_url(proceed_to_url: identity_email_url)
21
+ assert_redirected_to edit_identity_email_url
22
+ assert_equal "The password you entered is incorrect", flash[:alert]
30
23
  end
31
24
  end
@@ -6,9 +6,6 @@ class Identity::PasswordResetsControllerTest < ActionDispatch::IntegrationTest
6
6
  @sid = @<%= singular_table_name %>.signed_id(purpose: :password_reset, expires_in: 20.minutes)
7
7
  @sid_exp = @<%= singular_table_name %>.signed_id(purpose: :password_reset, expires_in: 0.minutes)
8
8
  end
9
- <%- if options.lockable? %>
10
- teardown { Kredis.clear_all }
11
- <%- end -%>
12
9
 
13
10
  test "should get new" do
14
11
  get new_identity_password_reset_url
@@ -8,6 +8,7 @@ class Identity::EmailsTest < ApplicationSystemTestCase
8
8
  test "updating the email" do
9
9
  click_on "Change email address"
10
10
 
11
+ fill_in "Current password", with: "Secret1*3*5*"
11
12
  fill_in "New email", with: "new_email@hey.com"
12
13
  click_on "Save changes"
13
14
 
@@ -5,9 +5,6 @@ class Identity::PasswordResetsTest < ApplicationSystemTestCase
5
5
  @<%= singular_table_name %> = <%= table_name %>(:lazaro_nixon)
6
6
  @sid = @<%= singular_table_name %>.signed_id(purpose: :password_reset, expires_in: 20.minutes)
7
7
  end
8
- <%- if options.lockable? %>
9
- teardown { Kredis.clear_all }
10
- <%- end -%>
11
8
 
12
9
  test "sending a password reset email" do
13
10
  visit sign_in_url
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: authentication-zero
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.10.0
4
+ version: 2.11.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nixon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-14 00:00:00.000000000 Z
11
+ date: 2022-03-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -19,6 +19,7 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - ".github/FUNDING.yml"
21
21
  - ".gitignore"
22
+ - ".rubocop.yml"
22
23
  - CHANGELOG.md
23
24
  - CODE_OF_CONDUCT.md
24
25
  - Gemfile
@@ -54,7 +55,6 @@ files:
54
55
  - lib/generators/authentication/templates/controllers/html/sessions/omniauth_controller.rb.tt
55
56
  - lib/generators/authentication/templates/controllers/html/sessions/sudos_controller.rb.tt
56
57
  - lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt
57
- - lib/generators/authentication/templates/controllers/html/sessions_controller_two_factor.rb.tt
58
58
  - lib/generators/authentication/templates/controllers/html/two_factor_authentication/challenges_controller.rb.tt
59
59
  - lib/generators/authentication/templates/controllers/html/two_factor_authentication/totps_controller.rb.tt
60
60
  - lib/generators/authentication/templates/erb/authentications/events/index.html.erb
@@ -90,21 +90,18 @@ files:
90
90
  - lib/generators/authentication/templates/test_unit/controllers/api/identity/password_resets_controller_test.rb.tt
91
91
  - lib/generators/authentication/templates/test_unit/controllers/api/passwords_controller_test.rb.tt
92
92
  - lib/generators/authentication/templates/test_unit/controllers/api/registrations_controller_test.rb.tt
93
- - lib/generators/authentication/templates/test_unit/controllers/api/sessions/sudos_controller_test.rb.tt
94
93
  - lib/generators/authentication/templates/test_unit/controllers/api/sessions_controller_test.rb.tt
95
94
  - lib/generators/authentication/templates/test_unit/controllers/html/identity/email_verifications_controller_test.rb.tt
96
95
  - lib/generators/authentication/templates/test_unit/controllers/html/identity/emails_controller_test.rb.tt
97
96
  - lib/generators/authentication/templates/test_unit/controllers/html/identity/password_resets_controller_test.rb.tt
98
97
  - lib/generators/authentication/templates/test_unit/controllers/html/passwords_controller_test.rb.tt
99
98
  - lib/generators/authentication/templates/test_unit/controllers/html/registrations_controller_test.rb.tt
100
- - lib/generators/authentication/templates/test_unit/controllers/html/sessions/sudos_controller_test.rb.tt
101
99
  - lib/generators/authentication/templates/test_unit/controllers/html/sessions_controller_test.rb.tt
102
100
  - lib/generators/authentication/templates/test_unit/fixtures.yml.tt
103
101
  - lib/generators/authentication/templates/test_unit/system/identity/emails_test.rb.tt
104
102
  - lib/generators/authentication/templates/test_unit/system/identity/password_resets_test.rb.tt
105
103
  - lib/generators/authentication/templates/test_unit/system/passwords_test.rb.tt
106
104
  - lib/generators/authentication/templates/test_unit/system/registrations_test.rb.tt
107
- - lib/generators/authentication/templates/test_unit/system/sessions/sudos_test.rb.tt
108
105
  - lib/generators/authentication/templates/test_unit/system/sessions_test.rb.tt
109
106
  - lib/generators/authentication/templates/test_unit/test_helper.rb.tt
110
107
  homepage: https://github.com/lazaronixon/authentication-zero
@@ -1,41 +0,0 @@
1
- class SessionsController < ApplicationController
2
- skip_before_action :authenticate, only: %i[ new create ]
3
-
4
- before_action :set_session, only: :destroy
5
-
6
- def index
7
- @sessions = Current.<%= singular_table_name %>.sessions.order(created_at: :desc)
8
- end
9
-
10
- def new
11
- @<%= singular_table_name %> = <%= class_name %>.new
12
- end
13
-
14
- def create
15
- <%= singular_table_name %> = <%= class_name %>.find_by(email: params[:email])
16
-
17
- if <%= singular_table_name %> && <%= singular_table_name %>.authenticate(params[:password])
18
- if <%= singular_table_name %>.otp_secret
19
- signed_id = <%= singular_table_name %>.signed_id(purpose: :authentication_challenge, expires_in: 20.minutes)
20
-
21
- redirect_to new_two_factor_authentication_challenge_path(token: signed_id)
22
- else
23
- @session = <%= singular_table_name %>.sessions.create!
24
- cookies.signed.permanent[:session_token] = { value: @session.id, httponly: true }
25
-
26
- redirect_to root_path, notice: "Signed in successfully"
27
- end
28
- else
29
- redirect_to sign_in_path(email_hint: params[:email]), alert: "That email or password is incorrect"
30
- end
31
- end
32
-
33
- def destroy
34
- @session.destroy; redirect_to(sessions_path, notice: "That session has been logged out")
35
- end
36
-
37
- private
38
- def set_session
39
- @session = Current.<%= singular_table_name %>.sessions.find(params[:id])
40
- end
41
- end
@@ -1,20 +0,0 @@
1
- require "test_helper"
2
-
3
- class Sessions::SudosControllerTest < ActionDispatch::IntegrationTest
4
- setup do
5
- @<%= singular_table_name %>, @token = sign_in_as(<%= table_name %>(:lazaro_nixon))
6
- @<%= singular_table_name %>.sessions.last.update! sudo_at: 1.day.ago
7
- end
8
-
9
- test "should sudo" do
10
- post sessions_sudo_url, params: { password: "Secret1*3*5*" }, headers: { "Authorization" => "Bearer #{@token}" }
11
- assert_response :no_content
12
- end
13
-
14
- test "should not sudo with wrong password" do
15
- post sessions_sudo_url, params: { password: "SecretWrong1*3" }, headers: { "Authorization" => "Bearer #{@token}" }
16
-
17
- assert_response :bad_request
18
- assert_equal "The password you entered is incorrect", response.parsed_body["error"]
19
- end
20
- end
@@ -1,22 +0,0 @@
1
- require "test_helper"
2
-
3
- class Sessions::SudosControllerTest < ActionDispatch::IntegrationTest
4
- setup do
5
- @<%= singular_table_name %> = sign_in_as(<%= table_name %>(:lazaro_nixon))
6
- end
7
-
8
- test "should get new" do
9
- get new_sessions_sudo_url(proceed_to_url: edit_password_url)
10
- assert_response :success
11
- end
12
-
13
- test "should sudo" do
14
- post sessions_sudo_url, params: { password: "Secret1*3*5*", proceed_to_url: edit_password_url }
15
- assert_redirected_to edit_password_url
16
- end
17
-
18
- test "should not sudo with wrong password" do
19
- post sessions_sudo_url, params: { password: "SecretWrong1*3", proceed_to_url: edit_password_url }
20
- assert_redirected_to new_sessions_sudo_url(proceed_to_url: edit_password_url)
21
- end
22
- end
@@ -1,15 +0,0 @@
1
- require "application_system_test_case"
2
-
3
- class Sessions::SudosTest < ApplicationSystemTestCase
4
- setup do
5
- @<%= singular_table_name %> = sign_in_as(<%= table_name %>(:lazaro_nixon))
6
- end
7
-
8
- test "executing sudo" do
9
- visit new_sessions_sudo_url(proceed_to_url: edit_password_url)
10
- fill_in :password, with: "Secret1*3*5*"
11
- click_on "Continue"
12
-
13
- assert_selector "h1", text: "Change your password"
14
- end
15
- end