authentication-zero 2.10.0 → 2.11.2

Sign up to get free protection for your applications and to get access to all the features.
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