authentication-zero 2.1.1 → 2.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/README.md +12 -7
  4. data/lib/authentication_zero/version.rb +1 -1
  5. data/lib/generators/authentication/authentication_generator.rb +3 -2
  6. data/lib/generators/authentication/templates/controllers/api/email_verifications_controller.rb.tt +18 -0
  7. data/lib/generators/authentication/templates/controllers/api/password_resets_controller.rb.tt +6 -6
  8. data/lib/generators/authentication/templates/controllers/api/registrations_controller.rb.tt +5 -1
  9. data/lib/generators/authentication/templates/controllers/api/sessions_controller.rb.tt +2 -2
  10. data/lib/generators/authentication/templates/controllers/html/email_verifications_controller.rb.tt +20 -0
  11. data/lib/generators/authentication/templates/controllers/html/password_resets_controller.rb.tt +6 -6
  12. data/lib/generators/authentication/templates/controllers/html/registrations_controller.rb.tt +14 -2
  13. data/lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt +4 -4
  14. data/lib/generators/authentication/templates/erb/emails/edit.html.erb.tt +7 -1
  15. data/lib/generators/authentication/templates/erb/identity_mailer/email_verify_confirmation.html.erb.tt +11 -0
  16. data/lib/generators/authentication/templates/erb/identity_mailer/email_verify_confirmation.text.erb.tt +9 -0
  17. data/lib/generators/authentication/templates/erb/{password_mailer/reset.html.erb.tt → identity_mailer/password_reset_provision.html.erb.tt} +1 -1
  18. data/lib/generators/authentication/templates/erb/{password_mailer/reset.text.erb.tt → identity_mailer/password_reset_provision.text.erb.tt} +1 -1
  19. data/lib/generators/authentication/templates/erb/session_mailer/{signed_in.html.erb.tt → signed_in_notification.html.erb.tt} +1 -1
  20. data/lib/generators/authentication/templates/erb/session_mailer/{signed_in.text.erb.tt → signed_in_notification.text.erb.tt} +1 -1
  21. data/lib/generators/authentication/templates/mailers/identity_mailer.rb.tt +15 -0
  22. data/lib/generators/authentication/templates/mailers/session_mailer.rb.tt +1 -1
  23. data/lib/generators/authentication/templates/migrations/create_sessions_migration.rb.tt +1 -0
  24. data/lib/generators/authentication/templates/migrations/create_table_migration.rb.tt +2 -0
  25. data/lib/generators/authentication/templates/models/current.rb.tt +1 -1
  26. data/lib/generators/authentication/templates/models/model.rb.tt +12 -0
  27. data/lib/generators/authentication/templates/models/session.rb.tt +1 -1
  28. data/lib/generators/authentication/templates/test_unit/controllers/api/email_verifications_controller_test.rb.tt +36 -0
  29. data/lib/generators/authentication/templates/test_unit/controllers/api/password_resets_controller_test.rb.tt +14 -3
  30. data/lib/generators/authentication/templates/test_unit/controllers/api/registrations_controller_test.rb.tt +15 -0
  31. data/lib/generators/authentication/templates/test_unit/controllers/api/sessions_controller_test.rb.tt +1 -1
  32. data/lib/generators/authentication/templates/test_unit/controllers/html/email_verifications_controller_test.rb.tt +35 -0
  33. data/lib/generators/authentication/templates/test_unit/controllers/html/password_resets_controller_test.rb.tt +14 -3
  34. data/lib/generators/authentication/templates/test_unit/controllers/html/registrations_controller_test.rb.tt +14 -0
  35. data/lib/generators/authentication/templates/test_unit/controllers/html/sessions_controller_test.rb.tt +1 -1
  36. data/lib/generators/authentication/templates/test_unit/fixtures.yml.tt +1 -0
  37. data/lib/generators/authentication/templates/test_unit/system/emails_test.rb.tt +10 -1
  38. data/lib/generators/authentication/templates/test_unit/system/registrations_test.rb.tt +20 -0
  39. data/lib/generators/authentication/templates/test_unit/system/sessions_test.rb.tt +1 -1
  40. metadata +13 -13
  41. data/lib/generators/authentication/templates/controllers/api/cancellations_controller.rb.tt +0 -5
  42. data/lib/generators/authentication/templates/controllers/html/cancellations_controller.rb.tt +0 -9
  43. data/lib/generators/authentication/templates/erb/cancellations/new.html.erb.tt +0 -11
  44. data/lib/generators/authentication/templates/mailers/password_mailer.rb.tt +0 -6
  45. data/lib/generators/authentication/templates/test_unit/controllers/api/cancellations_controller_test.rb.tt +0 -20
  46. data/lib/generators/authentication/templates/test_unit/controllers/html/cancellations_controller_test.rb.tt +0 -24
  47. data/lib/generators/authentication/templates/test_unit/system/cancellations_test.rb.tt +0 -23
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 44c36959694f55d09ae33d82d3dbe2091a49372c4792eda84bfddf94c62b636c
4
- data.tar.gz: 9d4b642209fe681865973ad1a5b13bd8bba1bf63140ae49acfe25923ce085514
3
+ metadata.gz: b27407c78924deb810f7fbfce18aee02eece09440bd24f4820a4533cb7117155
4
+ data.tar.gz: 26f8c2e4cd81245f09b5b72c7f2bae9293f2ce9e6d459ca93346fb87c10f20ad
5
5
  SHA512:
6
- metadata.gz: cbc64b8248e0bf392983bb19a3034b76471dc54511cb7ffee7ed122bb3c7f1424ec4211b11b5d0026395d622c1e6d6b35c3be09710f5617f45db2daece00ec49
7
- data.tar.gz: f3ccd41d8f2c417918cf65c7124fc597355d07bc96ba509bba12174d5161df33b2434a3af76a67f2e23de020f7cec549fc8a326619643373e4c05eb1b799c4bd
6
+ metadata.gz: dce2fef3a2d068f362ea47d2dee94d86627e80503a002d15ebe7beec21d178b0cceef74b6fddfe954388281aab4a4f6797997bdc542ca5f31c713c6e78cfc52d
7
+ data.tar.gz: fd70bc3d6585b769d3ba044fbe36a9f49e6a0954ce56b9cc5032f558cd6420992aef2d8a4ddb9d4bd719d5909031e58ebcf192f20b9c1e124af5a5abb4fb1dae
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- authentication-zero (2.1.1)
4
+ authentication-zero (2.2.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -4,14 +4,17 @@ The purpose of authentication zero is to generate a pre-built authentication sys
4
4
 
5
5
  ## Features
6
6
 
7
- - **Simplest code ever**
7
+ - **Simplest code ever (~200 lines of code)**
8
+ - **Inspired by hey.com**
8
9
  - Sign up
9
10
  - Email and password validations
10
11
  - Reset the user password and send reset instructions
12
+ - Reset the user password only from verified emails
11
13
  - Authentication by cookie (html)
12
14
  - Authentication by token (api)
13
- - Send e-mail when sign-in to your account
14
- - Manage multiple sessions
15
+ - Send e-mail verification when your email is changed
16
+ - Send e-mail when someone has signed-in into your account
17
+ - Manage multiple sessions & devices
15
18
  - Cancel my account
16
19
  - Log out
17
20
 
@@ -55,21 +58,23 @@ Add these lines to your `app/views/home/index.html.erb`:
55
58
  <p>Signed as <%= Current.user.email %></p>
56
59
 
57
60
  <div>
58
- <%= link_to "Change password", edit_passwords_path %>
61
+ <%= link_to "Change password", edit_password_path %>
59
62
  </div>
60
63
 
61
64
  <div>
62
- <%= link_to "Change email", edit_emails_path %>
65
+ <%= link_to "Change email address", edit_email_path %>
63
66
  </div>
64
67
 
65
68
  <div>
66
- <%= link_to "Manage Sessions", sessions_path %>
69
+ <%= link_to "Devices & Sessions", sessions_path %>
67
70
  </div>
68
71
 
69
72
  <div>
70
- <%= link_to "Cancel my account & delete my data", new_cancellations_path %>
73
+ <%= button_to "Cancel my account & delete my data", registration_path, method: :delete %>
71
74
  </div>
72
75
 
76
+ <br>
77
+
73
78
  <%= button_to "Log out", Current.session, method: :delete %>
74
79
  ```
75
80
 
@@ -1,3 +1,3 @@
1
1
  module AuthenticationZero
2
- VERSION = "2.1.1"
2
+ VERSION = "2.2.3"
3
3
  end
@@ -81,8 +81,8 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
81
81
 
82
82
  def create_views
83
83
  if options.api
84
+ directory "erb/identity_mailer", "app/views/identity_mailer"
84
85
  directory "erb/session_mailer", "app/views/session_mailer"
85
- directory "erb/password_mailer", "app/views/password_mailer"
86
86
  else
87
87
  directory "#{template_engine}", "app/views"
88
88
  end
@@ -94,9 +94,10 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
94
94
 
95
95
  def add_routes
96
96
  unless options.skip_routes
97
+ route "resource :registration, only: :destroy"
97
98
  route "resource :password_reset, only: [:new, :edit, :create, :update]"
98
- route "resource :cancellation, only: [:new, :create]"
99
99
  route "resource :password, only: [:edit, :update]"
100
+ route "resource :email_verification, only: [:new, :edit, :create, :update]"
100
101
  route "resource :email, only: [:edit, :update]"
101
102
  route "resources :sessions, only: [:index, :show, :destroy]"
102
103
  route "post 'sign_up', to: 'registrations#create'"
@@ -0,0 +1,18 @@
1
+ class EmailVerificationsController < ApplicationController
2
+ before_action :set_<%= singular_table_name %>, only: :update
3
+
4
+ def create
5
+ IdentityMailer.with(<%= singular_table_name %>: Current.<%= singular_table_name %>).email_verify_confirmation.deliver_later
6
+ end
7
+
8
+ def update
9
+ @<%= singular_table_name %>.update! verified: true
10
+ end
11
+
12
+ private
13
+ def set_<%= singular_table_name %>
14
+ @<%= singular_table_name %> = <%= class_name %>.find_signed!(params[:token], purpose: "verify_#{params[:email]}")
15
+ rescue ActiveSupport::MessageVerifier::InvalidSignature
16
+ render json: { error: "That email verification link is invalid" }, status: :bad_request
17
+ end
18
+ end
@@ -1,13 +1,13 @@
1
1
  class PasswordResetsController < ApplicationController
2
- skip_before_action :authenticate
3
-
4
2
  before_action :set_<%= singular_table_name %>, only: :update
5
3
 
4
+ skip_before_action :authenticate
5
+
6
6
  def create
7
- if @<%= singular_table_name %> = <%= class_name %>.find_by_email(params[:email])
8
- PasswordMailer.with(<%= singular_table_name %>: @<%= singular_table_name %>).reset.deliver_later
7
+ if @<%= singular_table_name %> = <%= class_name %>.find_by(email: params[:email], verified: true)
8
+ IdentityMailer.with(<%= singular_table_name %>: @<%= singular_table_name %>).password_reset_provision.deliver_later
9
9
  else
10
- render json: { error: "Sorry, we didn't recognize that email address" }, status: :not_found
10
+ render json: { error: "You can't reset your password until you verify your email" }, status: :not_found
11
11
  end
12
12
  end
13
13
 
@@ -23,7 +23,7 @@ class PasswordResetsController < ApplicationController
23
23
  def set_<%= singular_table_name %>
24
24
  @<%= singular_table_name %> = <%= class_name %>.find_signed!(params[:token], purpose: :password_reset)
25
25
  rescue ActiveSupport::MessageVerifier::InvalidSignature
26
- render json: { error: "Your token has expired, please request a new one" }, status: :bad_request
26
+ render json: { error: "That password reset link is invalid" }, status: :bad_request
27
27
  end
28
28
 
29
29
  def <%= "#{singular_table_name}_params" %>
@@ -1,5 +1,5 @@
1
1
  class RegistrationsController < ApplicationController
2
- skip_before_action :authenticate
2
+ skip_before_action :authenticate, only: :create
3
3
 
4
4
  def create
5
5
  @<%= singular_table_name %> = <%= class_name %>.new(<%= "#{singular_table_name}_params" %>)
@@ -11,6 +11,10 @@ class RegistrationsController < ApplicationController
11
11
  end
12
12
  end
13
13
 
14
+ def destroy
15
+ Current.<%= singular_table_name %>.destroy
16
+ end
17
+
14
18
  private
15
19
  def <%= "#{singular_table_name}_params" %>
16
20
  params.permit(:email, :password, :password_confirmation)
@@ -1,8 +1,8 @@
1
1
  class SessionsController < ApplicationController
2
- skip_before_action :authenticate, only: :create
3
-
4
2
  before_action :set_session, only: %i[ show destroy ]
5
3
 
4
+ skip_before_action :authenticate, only: :create
5
+
6
6
  def index
7
7
  render json: Current.<%= singular_table_name %>.sessions.order(created_at: :desc)
8
8
  end
@@ -0,0 +1,20 @@
1
+ class EmailVerificationsController < ApplicationController
2
+ before_action :set_<%= singular_table_name %>, only: :edit
3
+
4
+ def edit
5
+ @<%= singular_table_name %>.update! verified: true
6
+ redirect_to root_path, notice: "Thank you for verifying your email address"
7
+ end
8
+
9
+ def create
10
+ IdentityMailer.with(<%= singular_table_name %>: Current.<%= singular_table_name %>).email_verify_confirmation.deliver_later
11
+ redirect_to root_path, notice: "We sent a verification email to your email address"
12
+ end
13
+
14
+ private
15
+ def set_<%= singular_table_name %>
16
+ @<%= singular_table_name %> = <%= class_name %>.find_signed!(params[:token], purpose: "verify_#{params[:email]}")
17
+ rescue ActiveSupport::MessageVerifier::InvalidSignature
18
+ redirect_to edit_email_path, alert: "That email verification link is invalid"
19
+ end
20
+ end
@@ -1,8 +1,8 @@
1
1
  class PasswordResetsController < ApplicationController
2
- skip_before_action :authenticate
3
-
4
2
  before_action :set_<%= singular_table_name %>, only: %i[ edit update ]
5
3
 
4
+ skip_before_action :authenticate
5
+
6
6
  def new
7
7
  end
8
8
 
@@ -10,11 +10,11 @@ class PasswordResetsController < ApplicationController
10
10
  end
11
11
 
12
12
  def create
13
- if @<%= singular_table_name %> = <%= class_name %>.find_by_email(params[:email])
14
- PasswordMailer.with(<%= singular_table_name %>: @<%= singular_table_name %>).reset.deliver_later
13
+ if @<%= singular_table_name %> = <%= class_name %>.find_by(email: params[:email], verified: true)
14
+ IdentityMailer.with(<%= singular_table_name %>: @<%= singular_table_name %>).password_reset_provision.deliver_later
15
15
  redirect_to sign_in_path, notice: "Check your email for reset instructions"
16
16
  else
17
- redirect_to new_password_reset_path, alert: "Sorry, we didn't recognize that email address"
17
+ redirect_to new_password_reset_path, alert: "You can't reset your password until you verify your email"
18
18
  end
19
19
  end
20
20
 
@@ -30,7 +30,7 @@ class PasswordResetsController < ApplicationController
30
30
  def set_<%= singular_table_name %>
31
31
  @<%= singular_table_name %> = <%= class_name %>.find_signed!(params[:token], purpose: :password_reset)
32
32
  rescue ActiveSupport::MessageVerifier::InvalidSignature
33
- redirect_to new_password_reset_path, alert: "Your token has expired, please request a new one"
33
+ redirect_to new_password_reset_path, alert: "That password reset link is invalid"
34
34
  end
35
35
 
36
36
  def <%= "#{singular_table_name}_params" %>
@@ -1,5 +1,5 @@
1
1
  class RegistrationsController < ApplicationController
2
- skip_before_action :authenticate
2
+ skip_before_action :authenticate, only: %i[ new create ]
3
3
 
4
4
  def new
5
5
  @<%= singular_table_name %> = <%= class_name %>.new
@@ -9,14 +9,26 @@ class RegistrationsController < ApplicationController
9
9
  @<%= singular_table_name %> = <%= class_name %>.new(<%= "#{singular_table_name}_params" %>)
10
10
 
11
11
  if @<%= singular_table_name %>.save
12
- redirect_to sign_in_path, notice: "Welcome! You have signed up successfully"
12
+ session = @<%= singular_table_name %>.sessions.create!(session_params)
13
+ cookies.signed.permanent[:session_token] = { value: session.id, httponly: true }
14
+
15
+ redirect_to root_path, notice: "Welcome! You have signed up successfully"
13
16
  else
14
17
  render :new, status: :unprocessable_entity
15
18
  end
16
19
  end
17
20
 
21
+ def destroy
22
+ Current.<%= singular_table_name %>.destroy
23
+ redirect_to sign_in_path, notice: "Your account is closed"
24
+ end
25
+
18
26
  private
19
27
  def <%= "#{singular_table_name}_params" %>
20
28
  params.require(:<%= singular_table_name %>).permit(:email, :password, :password_confirmation)
21
29
  end
30
+
31
+ def session_params
32
+ { user_agent: request.user_agent, ip_address: request.remote_ip }
33
+ end
22
34
  end
@@ -1,8 +1,8 @@
1
1
  class SessionsController < ApplicationController
2
- skip_before_action :authenticate, only: %i[ new create ]
3
-
4
2
  before_action :set_session, only: :destroy
5
3
 
4
+ skip_before_action :authenticate, only: %i[ new create ]
5
+
6
6
  def index
7
7
  @sessions = Current.<%= singular_table_name %>.sessions.order(created_at: :desc)
8
8
  end
@@ -15,8 +15,8 @@ class SessionsController < ApplicationController
15
15
  @<%= singular_table_name %> = <%= class_name %>.find_by_email(params[:email])
16
16
 
17
17
  if @<%= singular_table_name %>.try(:authenticate, params[:password])
18
- @session = @<%= singular_table_name %>.sessions.create!(session_params)
19
- cookies.signed.permanent[:session_token] = { value: @session.id, httponly: true }
18
+ session = @<%= singular_table_name %>.sessions.create!(session_params)
19
+ cookies.signed.permanent[:session_token] = { value: session.id, httponly: true }
20
20
 
21
21
  redirect_to root_path, notice: "Signed in successfully"
22
22
  else
@@ -1,6 +1,12 @@
1
1
  <p style="color: red"><%%= alert %></p>
2
2
 
3
- <h1>Change your email</h1>
3
+ <%% if Current.<%= singular_table_name %>.verified? %>
4
+ <h1>Change your email</h1>
5
+ <%% else %>
6
+ <h1>Verify your email</h1>
7
+ <p>We sent a verification email to the address below. Check that email and follow those instructions to confirm it's your email address.</p>
8
+ <p><%%= button_to "Re-send verification email", email_verification_path %></p>
9
+ <%% end %>
4
10
 
5
11
  <%%= form_with(model: @<%= model_resource_name %>, url: email_path) do |form| %>
6
12
  <%% if @<%= singular_table_name %>.errors.any? %>
@@ -0,0 +1,11 @@
1
+ <p>Hey there,</p>
2
+
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
+
5
+ <p><strong>You must hit the link below to confirm that you received this email.</strong></p>
6
+
7
+ <%%= link_to "Yes, use this email for my account", edit_email_verification_url(token: @signed_id, email: @<%= singular_table_name %>.email) %>
8
+
9
+ <hr>
10
+
11
+ <p>Have questions or need help? Just reply to this email and our support team will help you sort it out.</p>
@@ -0,0 +1,9 @@
1
+ Hey there,
2
+
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
+
5
+ You must hit the link below to confirm that you received this email.
6
+
7
+ [Yes, use this email for my account]<%%= edit_email_verification_url(token: @signed_id, email: @<%= singular_table_name %>.email) %>
8
+
9
+ Have questions or need help? Just reply to this email and our support team will help you sort it out.
@@ -1,6 +1,6 @@
1
1
  <p>Hey there,</p>
2
2
 
3
- <p>Can't remember your password for <strong><%%= params[:<%= singular_table_name %>].email %></strong>? That's OK, it happens. Just hit the link below to set a new one.</p>
3
+ <p>Can't remember your password for <strong><%%= @session.<%= singular_table_name %>.email %></strong>? That's OK, it happens. Just hit the link below to set a new one.</p>
4
4
 
5
5
  <p><%%= link_to "Reset my password", edit_password_reset_url(token: @signed_id) %></p>
6
6
 
@@ -1,6 +1,6 @@
1
1
  Hey there,
2
2
 
3
- Can't remember your password for <%%= params[:<%= singular_table_name %>].email %>? That's OK, it happens. Just hit the link below to set a new one.
3
+ Can't remember your password for <%%= @session.<%= singular_table_name %>.email %>? That's OK, it happens. Just hit the link below to set a new one.
4
4
 
5
5
  [Reset my password]<%%= edit_password_reset_url(token: @signed_id) %>
6
6
 
@@ -10,7 +10,7 @@
10
10
  IP address: <%%= @session.ip_address %>
11
11
  </p>
12
12
 
13
- <p><strong>If this was you, carry on.</strong> We won't notify you about sign-ins from this device again.</p>
13
+ <p><strong>If this was you, carry on.</strong> We could notify you about sign-ins from this device again.</p>
14
14
 
15
15
  <p><strong>If you don't recognize this device</strong>, someone else may have accessed your account. You should immediately <%%= link_to "change your password", new_password_reset_url %>.</p>
16
16
 
@@ -8,7 +8,7 @@ A new device just signed in to your account (<%%= @session.<%= singular_table_na
8
8
 
9
9
  <%%= @session.ip_address %>
10
10
 
11
- If this was you, carry on. We won't notify you about sign-ins from this device again.
11
+ If this was you, carry on. We could notify you about sign-ins from this device again.
12
12
 
13
13
  If you don't recognize this device, someone else may have accessed your account. You should immediately [change your password]<%%= new_password_reset_url %>.
14
14
 
@@ -0,0 +1,15 @@
1
+ class IdentityMailer < ApplicationMailer
2
+ def password_reset_provision
3
+ @<%= singular_table_name %> = params[:<%= singular_table_name %>]
4
+ @signed_id = params[:<%= singular_table_name %>].signed_id(purpose: :password_reset, expires_in: 20.minutes)
5
+
6
+ mail to: @<%= singular_table_name %>.email, subject: "Reset your password"
7
+ end
8
+
9
+ def email_verify_confirmation
10
+ @<%= singular_table_name %> = params[:<%= singular_table_name %>]
11
+ @signed_id = params[:<%= singular_table_name %>].signed_id(purpose: "verify_#{@<%= singular_table_name %>.email}", expires_in: 20.minutes)
12
+
13
+ mail to: @<%= singular_table_name %>.email, subject: "Verify your email"
14
+ end
15
+ end
@@ -1,5 +1,5 @@
1
1
  class SessionMailer < ApplicationMailer
2
- def signed_in
2
+ def signed_in_notification
3
3
  @session = params[:session]
4
4
  mail to: @session.<%= singular_table_name %>.email, subject: "New sign-in to your account"
5
5
  end
@@ -2,6 +2,7 @@ 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
+
5
6
  t.string :user_agent
6
7
  t.string :ip_address
7
8
 
@@ -4,6 +4,8 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
4
4
  t.string :email, null: false
5
5
  t.string :password_digest, null: false
6
6
 
7
+ t.boolean :verified, default: false
8
+
7
9
  t.timestamps
8
10
  end
9
11
 
@@ -2,6 +2,6 @@ class Current < ActiveSupport::CurrentAttributes
2
2
  attribute :session, :<%= singular_table_name %>
3
3
 
4
4
  def session=(session)
5
- super; Current.<%= singular_table_name %> = session.<%= singular_table_name %>
5
+ super; self.<%= singular_table_name %> = session.<%= singular_table_name %>
6
6
  end
7
7
  end
@@ -10,4 +10,16 @@ class <%= class_name %> < ApplicationRecord
10
10
  before_validation do
11
11
  self.email = email.downcase.strip
12
12
  end
13
+
14
+ after_create_commit do
15
+ IdentityMailer.with(<%= singular_table_name %>: self).email_verify_confirmation.deliver_later
16
+ end
17
+
18
+ after_update_commit if: :email_previously_changed? do
19
+ update_columns verified: false
20
+ end
21
+
22
+ after_update_commit if: :email_previously_changed? do
23
+ IdentityMailer.with(<%= singular_table_name %>: self).email_verify_confirmation.deliver_later
24
+ end
13
25
  end
@@ -2,6 +2,6 @@ class Session < ApplicationRecord
2
2
  belongs_to :<%= singular_table_name %>
3
3
 
4
4
  after_create_commit do
5
- SessionMailer.with(session: self).signed_in.deliver_later
5
+ SessionMailer.with(session: self).signed_in_notification.deliver_later
6
6
  end
7
7
  end
@@ -0,0 +1,36 @@
1
+ require "test_helper"
2
+
3
+ class EmailVerificationsControllerTest < ActionDispatch::IntegrationTest
4
+ setup do
5
+ @<%= singular_table_name %>, @token = sign_in_as(<%= table_name %>(:lazaro_nixon))
6
+ @sid = @<%= singular_table_name %>.signed_id(purpose: "verify_#{@<%= singular_table_name %>.email}", expires_in: 20.minutes)
7
+ @sid_exp = @<%= singular_table_name %>.signed_id(purpose: "verify_#{@<%= singular_table_name %>.email}", expires_in: 0.minutes)
8
+
9
+ @<%= singular_table_name %>.update! verified: false
10
+ end
11
+
12
+ test "should send a verification email" do
13
+ assert_enqueued_email_with IdentityMailer, :email_verify_confirmation, args: { <%= singular_table_name %>: @<%= singular_table_name %> } do
14
+ post email_verification_url, headers: { "Authorization" => "Bearer #{@token}" }
15
+ end
16
+
17
+ assert_response :no_content
18
+ end
19
+
20
+ test "should verify email" do
21
+ patch email_verification_url, params: { token: @sid, email: @<%= singular_table_name %>.email }, headers: { "Authorization" => "Bearer #{@token}" }
22
+ assert_response :no_content
23
+ end
24
+
25
+ test "should not verify email with expired token" do
26
+ patch email_verification_url, params: { token: @sid_exp, email: @<%= singular_table_name %>.email }, headers: { "Authorization" => "Bearer #{@token}" }
27
+
28
+ assert_response :bad_request
29
+ assert_equal "That email verification link is invalid", response.parsed_body["error"]
30
+ end
31
+
32
+ def sign_in_as(<%= singular_table_name %>)
33
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "secret123" })
34
+ [<%= singular_table_name %>, response.headers["X-Session-Token"]]
35
+ end
36
+ end
@@ -8,7 +8,7 @@ class PasswordResetsControllerTest < ActionDispatch::IntegrationTest
8
8
  end
9
9
 
10
10
  test "should send a password reset email" do
11
- assert_enqueued_email_with PasswordMailer, :reset, args: { <%= singular_table_name %>: @<%= singular_table_name %> } do
11
+ assert_enqueued_email_with IdentityMailer, :password_reset_provision, args: { <%= singular_table_name %>: @<%= singular_table_name %> } do
12
12
  post password_reset_url, params: { email: @<%= singular_table_name %>.email }
13
13
  end
14
14
 
@@ -21,7 +21,18 @@ class PasswordResetsControllerTest < ActionDispatch::IntegrationTest
21
21
  end
22
22
 
23
23
  assert_response :not_found
24
- assert_equal "Sorry, we didn't recognize that email address", response.parsed_body["error"]
24
+ assert_equal "You can't reset your password until you verify your email", response.parsed_body["error"]
25
+ end
26
+
27
+ test "should not send a password reset email to a unverified email" do
28
+ @<%= singular_table_name %>.update!(verified: false)
29
+
30
+ assert_no_enqueued_emails do
31
+ post password_reset_url, params: { email: @<%= singular_table_name %>.email }
32
+ end
33
+
34
+ assert_response :not_found
35
+ assert_equal "You can't reset your password until you verify your email", response.parsed_body["error"]
25
36
  end
26
37
 
27
38
  test "should update password" do
@@ -33,6 +44,6 @@ class PasswordResetsControllerTest < ActionDispatch::IntegrationTest
33
44
  patch password_reset_url, params: { token: @sid_exp, password: "new_password", password_confirmation: "new_password" }
34
45
 
35
46
  assert_response :bad_request
36
- assert_equal "Your token has expired, please request a new one", response.parsed_body["error"]
47
+ assert_equal "That password reset link is invalid", response.parsed_body["error"]
37
48
  end
38
49
  end
@@ -8,4 +8,19 @@ class RegistrationsControllerTest < ActionDispatch::IntegrationTest
8
8
 
9
9
  assert_response :created
10
10
  end
11
+
12
+ test "should destroy account" do
13
+ @<%= singular_table_name %>, @token = sign_in_as(<%= table_name %>(:lazaro_nixon))
14
+
15
+ assert_difference("<%= class_name %>.count", -1) do
16
+ delete registration_url, headers: { "Authorization" => "Bearer #{@token}" }
17
+ end
18
+
19
+ assert_response :no_content
20
+ end
21
+
22
+ def sign_in_as(<%= singular_table_name %>)
23
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "secret123" })
24
+ [<%= singular_table_name %>, response.headers["X-Session-Token"]]
25
+ end
11
26
  end
@@ -18,7 +18,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
18
18
  test "should sign in" do
19
19
  post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "secret123" }
20
20
 
21
- assert_enqueued_email_with SessionMailer, :signed_in, args: { session: @<%= singular_table_name %>.sessions.last }
21
+ assert_enqueued_email_with SessionMailer, :signed_in_notification, args: { session: @<%= singular_table_name %>.sessions.last }
22
22
  assert_response :created
23
23
  end
24
24
 
@@ -0,0 +1,35 @@
1
+ require "test_helper"
2
+
3
+ class EmailVerificationsControllerTest < ActionDispatch::IntegrationTest
4
+ setup do
5
+ @<%= singular_table_name %> = sign_in_as(<%= table_name %>(:lazaro_nixon))
6
+ @sid = @<%= singular_table_name %>.signed_id(purpose: "verify_#{@<%= singular_table_name %>.email}", expires_in: 20.minutes)
7
+ @sid_exp = @<%= singular_table_name %>.signed_id(purpose: "verify_#{@<%= singular_table_name %>.email}", expires_in: 0.minutes)
8
+
9
+ @<%= singular_table_name %>.update! verified: false
10
+ end
11
+
12
+ test "should send a verification email" do
13
+ assert_enqueued_email_with IdentityMailer, :email_verify_confirmation, args: { <%= singular_table_name %>: @<%= singular_table_name %> } do
14
+ post email_verification_url
15
+ end
16
+
17
+ assert_redirected_to root_path
18
+ end
19
+
20
+ test "should verify email" do
21
+ get edit_email_verification_url(token: @sid, email: @<%= singular_table_name %>.email)
22
+ assert_redirected_to root_path
23
+ end
24
+
25
+ test "should not verify email with expired token" do
26
+ get edit_email_verification_url(token: @sid_exp, email: @<%= singular_table_name %>.email)
27
+
28
+ assert_redirected_to edit_email_path
29
+ assert_equal "That email verification link is invalid", flash[:alert]
30
+ end
31
+
32
+ def sign_in_as(<%= singular_table_name %>)
33
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "secret123" }); <%= singular_table_name %>
34
+ end
35
+ end
@@ -18,7 +18,7 @@ class PasswordResetsControllerTest < ActionDispatch::IntegrationTest
18
18
  end
19
19
 
20
20
  test "should send a password reset email" do
21
- assert_enqueued_email_with PasswordMailer, :reset, args: { <%= singular_table_name %>: @<%= singular_table_name %> } do
21
+ assert_enqueued_email_with IdentityMailer, :password_reset_provision, args: { <%= singular_table_name %>: @<%= singular_table_name %> } do
22
22
  post password_reset_url, params: { email: @<%= singular_table_name %>.email }
23
23
  end
24
24
 
@@ -31,7 +31,18 @@ class PasswordResetsControllerTest < ActionDispatch::IntegrationTest
31
31
  end
32
32
 
33
33
  assert_redirected_to new_password_reset_url
34
- assert_equal "Sorry, we didn't recognize that email address", flash[:alert]
34
+ assert_equal "You can't reset your password until you verify your email", flash[:alert]
35
+ end
36
+
37
+ test "should not send a password reset email to a unverified email" do
38
+ @<%= singular_table_name %>.update!(verified: false)
39
+
40
+ assert_no_enqueued_emails do
41
+ post password_reset_url, params: { email: @<%= singular_table_name %>.email }
42
+ end
43
+
44
+ assert_redirected_to new_password_reset_url
45
+ assert_equal "You can't reset your password until you verify your email", flash[:alert]
35
46
  end
36
47
 
37
48
  test "should update password" do
@@ -43,6 +54,6 @@ class PasswordResetsControllerTest < ActionDispatch::IntegrationTest
43
54
  patch password_reset_url, params: { token: @sid_exp, password: "new_password", password_confirmation: "new_password" }
44
55
 
45
56
  assert_redirected_to new_password_reset_path
46
- assert_equal "Your token has expired, please request a new one", flash[:alert]
57
+ assert_equal "That password reset link is invalid", flash[:alert]
47
58
  end
48
59
  end
@@ -11,6 +11,20 @@ class RegistrationsControllerTest < ActionDispatch::IntegrationTest
11
11
  post sign_up_url, params: { <%= singular_table_name %>: { email: "lazaronixon@hey.com", password: "secret123", password_confirmation: "secret123" } }
12
12
  end
13
13
 
14
+ assert_redirected_to root_url
15
+ end
16
+
17
+ test "should destroy account" do
18
+ sign_in_as <%= table_name %>(:lazaro_nixon)
19
+
20
+ assert_difference("<%= class_name %>.count", -1) do
21
+ delete registration_path
22
+ end
23
+
14
24
  assert_redirected_to sign_in_url
15
25
  end
26
+
27
+ def sign_in_as(<%= singular_table_name %>)
28
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "secret123" }); <%= singular_table_name %>
29
+ end
16
30
  end
@@ -19,7 +19,7 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
19
19
 
20
20
  test "should sign in" do
21
21
  post sign_in_url, params: { email: @<%= singular_table_name %>.email, password: "secret123" }
22
- assert_enqueued_email_with SessionMailer, :signed_in, args: { session: @<%= singular_table_name %>.sessions.last }
22
+ assert_enqueued_email_with SessionMailer, :signed_in_notification, args: { session: @<%= singular_table_name %>.sessions.last }
23
23
 
24
24
  assert_redirected_to root_url
25
25
 
@@ -3,3 +3,4 @@
3
3
  lazaro_nixon:
4
4
  email: lazaronixon@hotmail.com
5
5
  password_digest: <%%= BCrypt::Password.create("secret123") %>
6
+ verified: true
@@ -6,7 +6,7 @@ class EmailsTest < ApplicationSystemTestCase
6
6
  end
7
7
 
8
8
  test "updating the email" do
9
- click_on "Change email"
9
+ click_on "Change email address"
10
10
 
11
11
  fill_in "Current password", with: "secret123"
12
12
  fill_in "New email", with: "new_email@hey.com"
@@ -15,6 +15,15 @@ class EmailsTest < ApplicationSystemTestCase
15
15
  assert_text "Your email has been changed"
16
16
  end
17
17
 
18
+ test "sending a verification email" do
19
+ @<%= singular_table_name %>.update! verified: false
20
+
21
+ click_on "Change email address"
22
+ click_on "Re-send verification email"
23
+
24
+ assert_text "We sent a verification email to your email address"
25
+ end
26
+
18
27
  def sign_in_as(<%= singular_table_name %>)
19
28
  visit sign_in_url
20
29
  fill_in :email, with: <%= singular_table_name %>.email
@@ -1,6 +1,10 @@
1
1
  require "application_system_test_case"
2
2
 
3
3
  class RegistrationsTest < ApplicationSystemTestCase
4
+ setup do
5
+ @<%= singular_table_name %> = <%= table_name %>(:lazaro_nixon)
6
+ end
7
+
4
8
  test "signing up" do
5
9
  visit sign_in_url
6
10
  click_on "Sign up"
@@ -12,4 +16,20 @@ class RegistrationsTest < ApplicationSystemTestCase
12
16
 
13
17
  assert_text "Welcome! You have signed up successfully"
14
18
  end
19
+
20
+ test "cancelling my account" do
21
+ sign_in_as @<%= singular_table_name %>
22
+ click_on "Cancel my account & delete my data"
23
+
24
+ assert_text "Your account is closed"
25
+ end
26
+
27
+ def sign_in_as(<%= singular_table_name %>)
28
+ visit sign_in_url
29
+ fill_in :email, with: <%= singular_table_name %>.email
30
+ fill_in :password, with: "secret123"
31
+ click_on "Sign in"
32
+
33
+ return <%= singular_table_name %>
34
+ end
15
35
  end
@@ -8,7 +8,7 @@ class SessionsTest < ApplicationSystemTestCase
8
8
  test "visiting the index" do
9
9
  sign_in_as @<%= singular_table_name %>
10
10
 
11
- click_on "Manage Sessions"
11
+ click_on "Devices & Sessions"
12
12
  assert_selector "h1", text: "Sessions"
13
13
  end
14
14
 
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.1.1
4
+ version: 2.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nixon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-23 00:00:00.000000000 Z
11
+ date: 2022-02-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -31,44 +31,45 @@ files:
31
31
  - lib/authentication_zero/version.rb
32
32
  - lib/generators/authentication/USAGE
33
33
  - lib/generators/authentication/authentication_generator.rb
34
- - lib/generators/authentication/templates/controllers/api/cancellations_controller.rb.tt
34
+ - lib/generators/authentication/templates/controllers/api/email_verifications_controller.rb.tt
35
35
  - lib/generators/authentication/templates/controllers/api/emails_controller.rb.tt
36
36
  - lib/generators/authentication/templates/controllers/api/password_resets_controller.rb.tt
37
37
  - lib/generators/authentication/templates/controllers/api/passwords_controller.rb.tt
38
38
  - lib/generators/authentication/templates/controllers/api/registrations_controller.rb.tt
39
39
  - lib/generators/authentication/templates/controllers/api/sessions_controller.rb.tt
40
- - lib/generators/authentication/templates/controllers/html/cancellations_controller.rb.tt
40
+ - lib/generators/authentication/templates/controllers/html/email_verifications_controller.rb.tt
41
41
  - lib/generators/authentication/templates/controllers/html/emails_controller.rb.tt
42
42
  - lib/generators/authentication/templates/controllers/html/password_resets_controller.rb.tt
43
43
  - lib/generators/authentication/templates/controllers/html/passwords_controller.rb.tt
44
44
  - lib/generators/authentication/templates/controllers/html/registrations_controller.rb.tt
45
45
  - lib/generators/authentication/templates/controllers/html/sessions_controller.rb.tt
46
- - lib/generators/authentication/templates/erb/cancellations/new.html.erb.tt
47
46
  - lib/generators/authentication/templates/erb/emails/edit.html.erb.tt
48
- - lib/generators/authentication/templates/erb/password_mailer/reset.html.erb.tt
49
- - lib/generators/authentication/templates/erb/password_mailer/reset.text.erb.tt
47
+ - lib/generators/authentication/templates/erb/identity_mailer/email_verify_confirmation.html.erb.tt
48
+ - lib/generators/authentication/templates/erb/identity_mailer/email_verify_confirmation.text.erb.tt
49
+ - lib/generators/authentication/templates/erb/identity_mailer/password_reset_provision.html.erb.tt
50
+ - lib/generators/authentication/templates/erb/identity_mailer/password_reset_provision.text.erb.tt
50
51
  - lib/generators/authentication/templates/erb/password_resets/edit.html.erb.tt
51
52
  - lib/generators/authentication/templates/erb/password_resets/new.html.erb.tt
52
53
  - lib/generators/authentication/templates/erb/passwords/edit.html.erb.tt
53
54
  - lib/generators/authentication/templates/erb/registrations/new.html.erb.tt
54
- - lib/generators/authentication/templates/erb/session_mailer/signed_in.html.erb.tt
55
- - lib/generators/authentication/templates/erb/session_mailer/signed_in.text.erb.tt
55
+ - lib/generators/authentication/templates/erb/session_mailer/signed_in_notification.html.erb.tt
56
+ - lib/generators/authentication/templates/erb/session_mailer/signed_in_notification.text.erb.tt
56
57
  - lib/generators/authentication/templates/erb/sessions/index.html.erb.tt
57
58
  - lib/generators/authentication/templates/erb/sessions/new.html.erb.tt
58
- - lib/generators/authentication/templates/mailers/password_mailer.rb.tt
59
+ - lib/generators/authentication/templates/mailers/identity_mailer.rb.tt
59
60
  - lib/generators/authentication/templates/mailers/session_mailer.rb.tt
60
61
  - lib/generators/authentication/templates/migrations/create_sessions_migration.rb.tt
61
62
  - lib/generators/authentication/templates/migrations/create_table_migration.rb.tt
62
63
  - lib/generators/authentication/templates/models/current.rb.tt
63
64
  - lib/generators/authentication/templates/models/model.rb.tt
64
65
  - lib/generators/authentication/templates/models/session.rb.tt
65
- - lib/generators/authentication/templates/test_unit/controllers/api/cancellations_controller_test.rb.tt
66
+ - lib/generators/authentication/templates/test_unit/controllers/api/email_verifications_controller_test.rb.tt
66
67
  - lib/generators/authentication/templates/test_unit/controllers/api/emails_controller_test.rb.tt
67
68
  - lib/generators/authentication/templates/test_unit/controllers/api/password_resets_controller_test.rb.tt
68
69
  - lib/generators/authentication/templates/test_unit/controllers/api/passwords_controller_test.rb.tt
69
70
  - lib/generators/authentication/templates/test_unit/controllers/api/registrations_controller_test.rb.tt
70
71
  - lib/generators/authentication/templates/test_unit/controllers/api/sessions_controller_test.rb.tt
71
- - lib/generators/authentication/templates/test_unit/controllers/html/cancellations_controller_test.rb.tt
72
+ - lib/generators/authentication/templates/test_unit/controllers/html/email_verifications_controller_test.rb.tt
72
73
  - lib/generators/authentication/templates/test_unit/controllers/html/emails_controller_test.rb.tt
73
74
  - lib/generators/authentication/templates/test_unit/controllers/html/password_resets_controller_test.rb.tt
74
75
  - lib/generators/authentication/templates/test_unit/controllers/html/passwords_controller_test.rb.tt
@@ -76,7 +77,6 @@ files:
76
77
  - lib/generators/authentication/templates/test_unit/controllers/html/sessions_controller_test.rb.tt
77
78
  - lib/generators/authentication/templates/test_unit/fixtures.yml.tt
78
79
  - lib/generators/authentication/templates/test_unit/sessions.yml.tt
79
- - lib/generators/authentication/templates/test_unit/system/cancellations_test.rb.tt
80
80
  - lib/generators/authentication/templates/test_unit/system/emails_test.rb.tt
81
81
  - lib/generators/authentication/templates/test_unit/system/password_resets_test.rb.tt
82
82
  - lib/generators/authentication/templates/test_unit/system/passwords_test.rb.tt
@@ -1,5 +0,0 @@
1
- class CancellationsController < ApplicationController
2
- def create
3
- Current.<%= singular_table_name %>.destroy
4
- end
5
- end
@@ -1,9 +0,0 @@
1
- class CancellationsController < ApplicationController
2
- def new
3
- end
4
-
5
- def create
6
- Current.<%= singular_table_name %>.destroy
7
- redirect_to sign_in_path, notice: "Your account is closed"
8
- end
9
- end
@@ -1,11 +0,0 @@
1
- <h1>Want to close your account?</h1>
2
-
3
- <p>Your account will be immediately closed. You won't be able to sign in anymore.</p>
4
- <p>Your data will be permanently deleted from our servers.</p>
5
- <p><%%= link_to "Back", root_path %></p>
6
-
7
- <br>
8
-
9
- <div>
10
- <%%= button_to "OK, close my account", cancellation_path %>
11
- </div>
@@ -1,6 +0,0 @@
1
- class PasswordMailer < ApplicationMailer
2
- def reset
3
- @signed_id = params[:<%= singular_table_name %>].signed_id(purpose: :password_reset, expires_in: 20.minutes)
4
- mail to: params[:<%= singular_table_name %>].email, subject: "Reset your password"
5
- end
6
- end
@@ -1,20 +0,0 @@
1
- require "test_helper"
2
-
3
- class CancellationsControllerTest < ActionDispatch::IntegrationTest
4
- setup do
5
- @<%= singular_table_name %>, @token = sign_in_as(<%= table_name %>(:lazaro_nixon))
6
- end
7
-
8
- test "should create cancellation" do
9
- assert_difference("<%= class_name %>.count", -1) do
10
- post cancellation_url, headers: { "Authorization" => "Bearer #{@token}" }
11
- end
12
-
13
- assert_response :no_content
14
- end
15
-
16
- def sign_in_as(<%= singular_table_name %>)
17
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "secret123" })
18
- [<%= singular_table_name %>, response.headers["X-Session-Token"]]
19
- end
20
- end
@@ -1,24 +0,0 @@
1
- require "test_helper"
2
-
3
- class CancellationsControllerTest < 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_cancellation_url
10
- assert_response :success
11
- end
12
-
13
- test "should create cancellation" do
14
- assert_difference("<%= class_name %>.count", -1) do
15
- post cancellation_url
16
- end
17
-
18
- assert_redirected_to sign_in_url
19
- end
20
-
21
- def sign_in_as(<%= singular_table_name %>)
22
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "secret123" }); <%= singular_table_name %>
23
- end
24
- end
@@ -1,23 +0,0 @@
1
- require "application_system_test_case"
2
-
3
- class CancellationsTest < ApplicationSystemTestCase
4
- setup do
5
- @<%= singular_table_name %> = sign_in_as(<%= table_name %>(:lazaro_nixon))
6
- end
7
-
8
- test "cancelling my account" do
9
- click_on "Cancel my account & delete my data"
10
- click_on "OK, close my account"
11
-
12
- assert_text "Your account is closed"
13
- end
14
-
15
- def sign_in_as(<%= singular_table_name %>)
16
- visit sign_in_url
17
- fill_in :email, with: <%= singular_table_name %>.email
18
- fill_in :password, with: "secret123"
19
- click_on "Sign in"
20
-
21
- return <%= singular_table_name %>
22
- end
23
- end