minimalist_authentication 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +44 -4
  3. data/app/controllers/email_verifications_controller.rb +16 -0
  4. data/app/controllers/emails_controller.rb +22 -0
  5. data/app/controllers/password_resets_controller.rb +30 -0
  6. data/app/controllers/passwords_controller.rb +34 -0
  7. data/app/mailers/application_mailer.rb +5 -0
  8. data/app/mailers/minimalist_authentication_mailer.rb +22 -0
  9. data/app/views/email_verifications/new.html.erb +14 -0
  10. data/app/views/email_verifications/show.html.erb +10 -0
  11. data/app/views/emails/edit.html.erb +12 -0
  12. data/app/views/layouts/mailer.html.erb +13 -0
  13. data/app/views/layouts/mailer.text.erb +1 -0
  14. data/app/views/minimalist_authentication_mailer/update_password.html.erb +3 -0
  15. data/app/views/minimalist_authentication_mailer/update_password.text.erb +3 -0
  16. data/app/views/minimalist_authentication_mailer/verify_email.html.erb +5 -0
  17. data/app/views/minimalist_authentication_mailer/verify_email.text.erb +5 -0
  18. data/app/views/password_resets/new.html.erb +13 -0
  19. data/app/views/passwords/edit.html.erb +17 -0
  20. data/app/views/sessions/_form.html.erb +9 -3
  21. data/app/views/sessions/new.html.erb +2 -0
  22. data/config/locales/minimalist_authentication.en.yml +7 -0
  23. data/config/routes.rb +8 -0
  24. data/lib/minimalist_authentication.rb +2 -0
  25. data/lib/minimalist_authentication/configuration.rb +32 -0
  26. data/lib/minimalist_authentication/email_verification.rb +41 -0
  27. data/lib/minimalist_authentication/sessions.rb +24 -4
  28. data/lib/minimalist_authentication/user.rb +17 -11
  29. data/lib/minimalist_authentication/verifiable_token.rb +51 -0
  30. data/lib/minimalist_authentication/version.rb +1 -1
  31. metadata +22 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c706bf1ccf629eedb3a910b68a983ae2d4869f4d
4
- data.tar.gz: 8d5f220c4f4ac6d91c445c3a946fc913ba5d1cbd
3
+ metadata.gz: 6646211204dbc15edbbe5a48d00546cdb826acde
4
+ data.tar.gz: 00dfd3515eb12f919abb0bff9da8e76823235065
5
5
  SHA512:
6
- metadata.gz: aeb1bfee96f489dd77baa1c3fa9059fdcc3e335cc9b50e387be72c5efd6f859f92c4827783e4a51722bf6482edfd0c1f961e43d6c65b93dd6f2467786aebb3d2
7
- data.tar.gz: a0e7282735c59607dc0373a8940885659b8df6981ffeefe6e47535f99cc23b0058ea4084a9a9233f73562811179a208f29628b744afdefdd593e468b1760d33e
6
+ metadata.gz: 06de541cfc8129ab8fc70e39b5a4d29cd565f4dfdd26a38225d40a7dc0d18bed65f0ef3b1fb6c7530af964ebb7e7393916a9c4247c4212f0e5f995c1471f6b8c
7
+ data.tar.gz: d21ffa2de1ff35cbea0d03cbacf59ad5b0e6163c802065c474d4d4b830d3d015c76f12f5d295a86d5d3603c44e7a63b47010cdcdc6144ce032a91a26f2753514
data/README.md CHANGED
@@ -65,10 +65,15 @@ end
65
65
  Customize the configuration with an initializer. Create a **minimalist_authentication.rb** file in /Users/baldwina/git/brightways/config/initializers.
66
66
  ```ruby
67
67
  MinimalistAuthentication.configure do |configuration|
68
- configuration.user_model_name = 'CustomModelName' # default is '::User'
69
- configuration.session_key = :custom_session_key # default is ':user_id'
70
- validate_email = true # default is true
71
- validate_email_presence = true # default is true
68
+ configuration.user_model_name = 'CustomModelName' # default is '::User'
69
+ configuration.session_key = :custom_session_key # default is :user_id
70
+ configuration.validate_email = true # default is true
71
+ configuration.validate_email_presence = true # default is true
72
+ configuration.request_email = true # default is true
73
+ configuration.verify_email = true # default is true
74
+ configuration.login_redirect_path = :custom_path # default is :root_path
75
+ configuration.logout_redirect_path = :custom_path # default is :new_session_path
76
+ configuration.email_prefix = '[Custom Prefix]' # default is application name
72
77
  end
73
78
  ```
74
79
 
@@ -83,6 +88,41 @@ example_user:
83
88
  ```
84
89
 
85
90
 
91
+ ## Verification Tokens
92
+ Verification token support is provided by the **MinimalistAuthentication::VerifiableToken**
93
+ module. Include the module in your user class and add the verification token columns
94
+ to the database.
95
+
96
+ Include MinimalistAuthentication::VerifiableToken in your user model (app/models/user.rb)
97
+ ```ruby
98
+ class User < ApplicationRecord
99
+ include MinimalistAuthentication::User
100
+ include MinimalistAuthentication::VerifiableToken
101
+ end
102
+ ```
103
+
104
+ Add the **verification_token** and **verification_token_generated_at** columns:
105
+ Create a user model with **email** for an identifier:
106
+ ```bash
107
+ bin/rails generate migration AddVerificationTokenToUsers verification_token:string:uniq verification_token_generated_at:datetime
108
+ ```
109
+
110
+ ### Email Verification
111
+ Include MinimalistAuthentication::EmailVerification in your user model (app/models/user.rb)
112
+ ```ruby
113
+ class User < ApplicationRecord
114
+ include MinimalistAuthentication::User
115
+ include MinimalistAuthentication::VerifiableToken
116
+ include MinimalistAuthentication::EmailVerification
117
+ end
118
+ ```
119
+
120
+ Add the **email_verified_at** column to your user model:
121
+ ```bash
122
+ bin/rails generate migration AddVerificationTokenToUsers verification_token:string:uniq verification_token_generated_at:datetime
123
+ ```
124
+
125
+
86
126
  ## Conversions
87
127
  Pre 2.0 versions of MinimalistAuthentication supported multiple hash algorithms
88
128
  and stored the hashed password and salt as separate fields in the database
@@ -0,0 +1,16 @@
1
+ class EmailVerificationsController < ApplicationController
2
+ def new
3
+ # verify email for current_user
4
+ end
5
+
6
+ def create
7
+ current_user.regenerate_verification_token
8
+ MinimalistAuthenticationMailer.verify_email(current_user).deliver_now
9
+
10
+ redirect_to dashboard_path, notice: "Verification email sent to #{current_user.email}, follow the instructions to complete verification. Thank you!"
11
+ end
12
+
13
+ def show
14
+ current_user.verify_email(params[:token])
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ class EmailsController < ApplicationController
2
+ def edit
3
+ end
4
+
5
+ def update
6
+ if current_user.update_attributes(user_params)
7
+ redirect_to update_redirect_path, notice: 'Email successfully updated'
8
+ else
9
+ render :edit
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def user_params
16
+ params.require(:user).permit(:email)
17
+ end
18
+
19
+ def update_redirect_path
20
+ current_user.needs_email_verification? ? new_email_verification_path : dashboard_path
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ class PasswordResetsController < ApplicationController
2
+ skip_before_action :authorization_required
3
+
4
+ layout 'sessions'
5
+
6
+ # Form for user to request a password reset
7
+ def new
8
+ @user = MinimalistAuthentication.configuration.user_model.new
9
+ end
10
+
11
+ # Send a password update link to users with a verified email
12
+ def create
13
+ if user
14
+ user.regenerate_verification_token
15
+ MinimalistAuthenticationMailer.update_password(user).deliver_now
16
+ end
17
+ # always display notice even if the user was not found to prevent leaking user emails
18
+ redirect_to new_session_path, notice: "Password reset instructions were mailed to #{email_params[:email]}"
19
+ end
20
+
21
+ private
22
+
23
+ def user
24
+ @user ||= MinimalistAuthentication.configuration.user_model.active.email_verified.find_by(email_params)
25
+ end
26
+
27
+ def email_params
28
+ params.require(:user).permit(:email)
29
+ end
30
+ end
@@ -0,0 +1,34 @@
1
+ class PasswordsController < ApplicationController
2
+ skip_before_action :authorization_required
3
+
4
+ layout 'sessions'
5
+
6
+ # From for user to update password
7
+ def edit
8
+ redirect_to(new_session_path) unless user.matches_verification_token?(token)
9
+ # render passwords/edit.html.erb
10
+ end
11
+
12
+ # Update user's password
13
+ def update
14
+ if user.secure_update(token, password_params.merge(password_required: true))
15
+ redirect_to new_session_path, notice: 'Password successfully updated'
16
+ else
17
+ render :edit
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def user
24
+ @user ||= User.find(params[:user_id])
25
+ end
26
+
27
+ def token
28
+ @token ||= params[:token]
29
+ end
30
+
31
+ def password_params
32
+ params.require(:user).permit(:password, :password_confirmation)
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ class ApplicationMailer < ActionMailer::Base
2
+ default from: 'from@example.com'
3
+ layout 'mailer'
4
+ end
5
+
@@ -0,0 +1,22 @@
1
+ class MinimalistAuthenticationMailer < ApplicationMailer
2
+ def verify_email(user)
3
+ @verify_email_link = email_verification_url(token: user.verification_token)
4
+ send_to(user, 'Email Address Verification')
5
+ end
6
+
7
+ def update_password(user)
8
+ @edit_password_link = edit_user_password_url(user, token: user.verification_token)
9
+ send_to(user, 'Update Password')
10
+ end
11
+
12
+ private
13
+
14
+ def send_to(user, subject)
15
+ @user = user
16
+ mail to: @user.email, subject: prefixed_subject(subject)
17
+ end
18
+
19
+ def prefixed_subject(subject)
20
+ "#{MinimalistAuthentication.configuration.email_prefix} #{subject}"
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ <h2>Please verify your email address</h2>
2
+
3
+ <p>
4
+ <strong><%= current_user.email %></strong>
5
+ <em><%= link_to('change', edit_email_path) %></em>
6
+ </p>
7
+
8
+ <%= form_tag email_verification_path do |form| %>
9
+ <%= submit_tag 'Send Verification Email' %>
10
+ <% end %>
11
+
12
+ <p>Verifying your email will allow you to receive confidential messages and reset your password.</p>
13
+
14
+ <%= link_to('Skip Email Verification', dashboard_path) %>
@@ -0,0 +1,10 @@
1
+ <h1>Email Verification</h1>
2
+
3
+ <% if current_user.email_verified? %>
4
+ <h2>Your email address has been verified</h2>
5
+ <% else %>
6
+ <h2>Email address verification failed</h2>
7
+ <% end %>
8
+ <h2><%= current_user.email %></h2>
9
+
10
+ <%= link_to('Go to Dashboard', dashboard_path) %>
@@ -0,0 +1,12 @@
1
+ <h1>Email Update</h1>
2
+
3
+ <h2>Please update your email address</h2>
4
+
5
+ <%= form_for(current_user, as: :user, url: email_path) do |form| %>
6
+ <%= form.text_field :email %>
7
+ <%= form.submit 'Update' %>
8
+ <% end %>
9
+
10
+ <p>Providing your email will allow you to receive confidential messages and reset your password.</p>
11
+
12
+ <%= link_to('Skip Email Update', dashboard_path) %>
@@ -0,0 +1,13 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5
+ <style>
6
+ /* Email styles need to be inline */
7
+ </style>
8
+ </head>
9
+
10
+ <body>
11
+ <%= yield %>
12
+ </body>
13
+ </html>
@@ -0,0 +1 @@
1
+ <%= yield %>
@@ -0,0 +1,3 @@
1
+ <p><%= t('.opening') %></p>
2
+
3
+ <%= link_to @edit_password_link, @edit_password_link %>
@@ -0,0 +1,3 @@
1
+ <%= t('.opening') %>
2
+
3
+ <%= @edit_password_link %>
@@ -0,0 +1,5 @@
1
+ <p><%= t('.opening') %></p>
2
+
3
+ <%= link_to @verify_email_link, @verify_email_link %>
4
+
5
+ <p><%= t('.closing') %></p>
@@ -0,0 +1,5 @@
1
+ <%= t('.opening') %>
2
+
3
+ <%= @verify_email_link %>
4
+
5
+ <%= t('.closing') %>
@@ -0,0 +1,13 @@
1
+ <h1>Request Password Reset</h1>
2
+
3
+ <p>
4
+ Enter your email address we'll send you a link to reset your password.
5
+ </p>
6
+
7
+ <%= form_for @user, as: :user, url: password_reset_path do |form| %>
8
+ <div>
9
+ <%= form.label :email %>
10
+ <%= form.text_field :email %>
11
+ </div>
12
+ <%= form.submit 'Reset Password' %>
13
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <h1>Edit Password</h1>
2
+
3
+ <%= form_for @user, as: :user, url: user_password_path(@user, token: @token), html: { method: :put } do |form| %>
4
+ <div>
5
+ <%= form.label :password %>
6
+ <%= form.password_field :password %>
7
+ </div>
8
+ <div>
9
+ <%= form.label :password_confirmation %>
10
+ <%= form.password_field :password_confirmation %>
11
+ </div>
12
+ <%= form.submit 'Update Password' %>
13
+ <% end %>
14
+
15
+ <div class="field_with_errors" style: 'border-width:5px;'>
16
+ testing
17
+ </div>
@@ -1,5 +1,11 @@
1
- <%= form_for :user, url: session_path do |form| %>
2
- <%= form.text_field :email %>
3
- <%= form.password_field :password %>
1
+ <%= form_for @user, url: session_path do |form| %>
2
+ <div>
3
+ <%= form.label :email %>
4
+ <%= form.text_field :email %>
5
+ </div>
6
+ <div>
7
+ <%= form.label :password %>
8
+ <%= form.password_field :password %>
9
+ </div>
4
10
  <%= form.submit 'Log in' %>
5
11
  <% end %>
@@ -1 +1,3 @@
1
+ <h1>Login</h1>
1
2
  <%= render partial: 'form' %>
3
+ <%= link_to 'I forgot', new_password_reset_path %>
@@ -0,0 +1,7 @@
1
+ en:
2
+ minimalist_authentication_mailer:
3
+ update_password:
4
+ opening: 'Please click the link below to update your password:'
5
+ verify_email:
6
+ opening: "Please click the link below to complete your email verification:"
7
+ closing: 'If you did not request email verification you can safely ignore this message.'
@@ -1,2 +1,10 @@
1
1
  Rails.application.routes.draw do
2
+ resources :user, only: [] do
3
+ resource :password, only: %i(edit update)
4
+ end
5
+
6
+ resource :password_reset, only: %i(new create)
7
+
8
+ resource :email, only: %i(edit update)
9
+ resource :email_verification, only: %i(new create show)
2
10
  end
@@ -1,6 +1,8 @@
1
1
  require 'minimalist_authentication/engine'
2
2
  require 'minimalist_authentication/configuration'
3
3
  require 'minimalist_authentication/user'
4
+ require 'minimalist_authentication/verifiable_token'
5
+ require 'minimalist_authentication/email_verification'
4
6
  require 'minimalist_authentication/password'
5
7
  require 'minimalist_authentication/null_password'
6
8
  require 'minimalist_authentication/controller'
@@ -32,11 +32,37 @@ module MinimalistAuthentication
32
32
  # Note: validate_email_presence is only checked if validate_email is true.
33
33
  attr_accessor :validate_email_presence
34
34
 
35
+ # Check for users email at login and request if blank. Only useful if using
36
+ # username to login and users might not have an email set.
37
+ # Defaults to true
38
+ attr_accessor :request_email
39
+
40
+ # Vefify users email address at login.
41
+ # Defaults to true.
42
+ attr_accessor :verify_email
43
+
44
+ # Where to route users after a successful login.
45
+ # Defaults to :root_path
46
+ attr_accessor :login_redirect_path
47
+
48
+ # Where to route users after logging out.
49
+ # Defaults to :new_session_path
50
+ attr_accessor :logout_redirect_path
51
+
52
+ # Email subject prefix for MinimalistAuthenticationMailer messages
53
+ # Defaults to application name
54
+ attr_accessor :email_prefix
55
+
35
56
  def initialize
36
57
  self.user_model_name = '::User'
37
58
  self.session_key = :user_id
38
59
  self.validate_email = true
39
60
  self.validate_email_presence = true
61
+ self.request_email = true
62
+ self.verify_email = true
63
+ self.login_redirect_path = :root_path
64
+ self.logout_redirect_path = :new_session_path
65
+ self.email_prefix = default_email_prefix
40
66
  end
41
67
 
42
68
  # Returns the user_model class
@@ -45,5 +71,11 @@ module MinimalistAuthentication
45
71
  def user_model
46
72
  @user_model ||= user_model_name.constantize
47
73
  end
74
+
75
+ private
76
+
77
+ def default_email_prefix
78
+ "[#{Rails.application.engine_name.gsub(/_application\z/, '').titleize}]"
79
+ end
48
80
  end
49
81
  end
@@ -0,0 +1,41 @@
1
+ module MinimalistAuthentication
2
+ module EmailVerification
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ before_save :clear_email_verification, if: ->(user) { user.email_changed? }
7
+
8
+ scope :email_verified, -> { where('LENGTH(email) > 2').where.not(email_verified_at: nil) }
9
+ end
10
+
11
+ def needs_email_set?
12
+ request_email_enabled? && email.blank?
13
+ end
14
+
15
+ def needs_email_verification?
16
+ email_verification_enabled? && email.present? && email_verified_at.blank?
17
+ end
18
+
19
+ def email_verified?
20
+ email.present? && email_verified_at.present?
21
+ end
22
+
23
+ def verify_email(token)
24
+ secure_update(token, email_verified_at: Time.zone.now)
25
+ end
26
+
27
+ private
28
+
29
+ def request_email_enabled?
30
+ MinimalistAuthentication.configuration.request_email
31
+ end
32
+
33
+ def email_verification_enabled?
34
+ MinimalistAuthentication.configuration.verify_email
35
+ end
36
+
37
+ def clear_email_verification
38
+ self.email_verified_at = nil
39
+ end
40
+ end
41
+ end
@@ -8,7 +8,7 @@ module MinimalistAuthentication
8
8
  end
9
9
 
10
10
  def new
11
- @user = MinimalistAuthentication.configuration.user_model.new
11
+ new_user
12
12
  end
13
13
 
14
14
  def create
@@ -16,7 +16,7 @@ module MinimalistAuthentication
16
16
  scrub_session!
17
17
  authenticated_user.logged_in
18
18
  session[MinimalistAuthentication.configuration.session_key] = authenticated_user.id
19
- after_authentication_success
19
+ set_or_verify_email || after_authentication_success
20
20
  return
21
21
  else
22
22
  after_authentication_failure
@@ -31,6 +31,10 @@ module MinimalistAuthentication
31
31
 
32
32
  private
33
33
 
34
+ def new_user
35
+ @user ||= MinimalistAuthentication.configuration.user_model.new
36
+ end
37
+
34
38
  def authenticated_user
35
39
  @authenticated_user ||= MinimalistAuthentication.configuration.user_model.authenticate(user_params)
36
40
  end
@@ -39,12 +43,28 @@ module MinimalistAuthentication
39
43
  @user_params ||= params.require(:user).permit(:email, :username, :password)
40
44
  end
41
45
 
46
+ def set_or_verify_email
47
+ if authenticated_user.needs_email_set?
48
+ redirect_to edit_email_path
49
+ elsif authenticated_user.needs_email_verification? && !attempting_to_verify?
50
+ redirect_to new_email_verification_path
51
+ else
52
+ false
53
+ end
54
+ end
55
+
42
56
  def after_authentication_success
43
57
  redirect_back_or_default(login_redirect_to)
44
58
  end
45
59
 
60
+ def attempting_to_verify?
61
+ # check if user is attpting to verify their email
62
+ session['return_to'].to_s[/token/]
63
+ end
64
+
46
65
  def after_authentication_failure
47
66
  flash.now[:alert] = "Couldn't log you in as '#{user_params[:email] || user_params[:username]}'"
67
+ new_user
48
68
  render :new
49
69
  end
50
70
 
@@ -55,11 +75,11 @@ module MinimalistAuthentication
55
75
  end
56
76
 
57
77
  def login_redirect_to
58
- root_path
78
+ send(MinimalistAuthentication.configuration.login_redirect_path)
59
79
  end
60
80
 
61
81
  def logout_redirect_to
62
- new_session_path
82
+ send(MinimalistAuthentication.configuration.logout_redirect_path)
63
83
  end
64
84
  end
65
85
  end
@@ -4,32 +4,37 @@ module MinimalistAuthentication
4
4
  module User
5
5
  extend ActiveSupport::Concern
6
6
 
7
- GUEST_USER_EMAIL = 'guest'
8
- EMAIL_REGEX = /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
7
+ GUEST_USER_EMAIL = 'guest'
8
+ EMAIL_REGEX = /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
9
+ PASSWORD_MIN = 8
10
+ PASSWORD_MAX = 40
9
11
 
10
12
  included do
11
13
  # Stores the plain text password.
12
14
  attr_accessor :password
13
15
 
16
+ # Force validations for a blank password.
17
+ attr_accessor :password_required
18
+
14
19
  # Hashes and stores the password on save.
15
20
  before_save :hash_password
16
21
 
17
22
  # Email validations
18
- validates_presence_of :email, if: :validate_email_presence?
19
- validates_uniqueness_of :email, allow_blank: true, if: :validate_email?
20
- validates_format_of :email, allow_blank: true, with: EMAIL_REGEX, if: :validate_email?
23
+ validates_presence_of :email, if: :validate_email_presence?
24
+ validates_uniqueness_of :email, allow_blank: true, if: :validate_email?
25
+ validates_format_of :email, allow_blank: true, with: EMAIL_REGEX, if: :validate_email?
21
26
 
22
27
  # Password validations
23
- validates_presence_of :password, if: :validate_password?
24
- validates_confirmation_of :password, if: :validate_password?
25
- validates_length_of :password, within: 8..40, if: :validate_password?
28
+ validates_presence_of :password, if: :validate_password?
29
+ validates_confirmation_of :password, if: :validate_password?
30
+ validates_length_of :password, within: PASSWORD_MIN..PASSWORD_MAX, if: :validate_password?
26
31
 
27
32
  # Active scope
28
33
  scope :active, ->(active = true) { where active: active }
29
34
  end
30
35
 
31
36
  module ClassMethods
32
- # Authenticates a user form the params provied. Expects a params hash with
37
+ # Authenticates a user form the params provided. Expects a params hash with
33
38
  # email or username and passwod keys.
34
39
  # Params examples:
35
40
  # { email: 'user@example.com', password: 'abc123' }
@@ -103,9 +108,10 @@ module MinimalistAuthentication
103
108
  end
104
109
 
105
110
  # Requre password for active users that either do no have a password hash
106
- # stored OR are attempting to set a new password.
111
+ # stored OR are attempting to set a new password. Set **password_required**
112
+ # to true to force validations even when the password field is blank.
107
113
  def validate_password?
108
- active? && (password_hash.blank? || password.present?)
114
+ active? && (password_hash.blank? || password.present? || password_required)
109
115
  end
110
116
 
111
117
  # Validate email for active users.
@@ -0,0 +1,51 @@
1
+ module MinimalistAuthentication
2
+ module VerifiableToken
3
+ extend ActiveSupport::Concern
4
+
5
+ TOKEN_EXPIRATION_HOURS = 6
6
+
7
+ # generate secure verification_token and record generation time
8
+ def regenerate_verification_token
9
+ update_token
10
+ end
11
+
12
+ def secure_update(token, attributes)
13
+ if matches_verification_token?(token)
14
+ update(attributes) && clear_token
15
+ else
16
+ errors.add(:base, 'Verfication token check failed')
17
+ return false
18
+ end
19
+ end
20
+
21
+ def matches_verification_token?(token)
22
+ verification_token_valid? && secure_match?(token)
23
+ end
24
+
25
+ def verification_token_valid?
26
+ return false if verification_token.blank? || verification_token_generated_at.blank?
27
+ verification_token_generated_at > TOKEN_EXPIRATION_HOURS.hours.ago
28
+ end
29
+
30
+ private
31
+
32
+ def clear_token
33
+ update_token(token: nil, time: nil)
34
+ end
35
+
36
+ def update_token(token: self.class.generate_unique_secure_token, time: Time.now.utc)
37
+ update!(
38
+ verification_token: token,
39
+ verification_token_generated_at: time
40
+ )
41
+ end
42
+
43
+ # Compare the tokens in a time-constant manner, to mitigate timing attacks.
44
+ def secure_match?(token)
45
+ ActiveSupport::SecurityUtils.secure_compare(
46
+ ::Digest::SHA256.hexdigest(token),
47
+ ::Digest::SHA256.hexdigest(verification_token)
48
+ )
49
+ end
50
+ end
51
+ end
@@ -1,3 +1,3 @@
1
1
  module MinimalistAuthentication
2
- VERSION = '2.0.0'
2
+ VERSION = '2.1.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minimalist_authentication
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Baldwin
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-09-18 00:00:00.000000000 Z
12
+ date: 2017-10-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -71,19 +71,39 @@ files:
71
71
  - README.md
72
72
  - Rakefile
73
73
  - app/assets/config/minimalist_authentication_manifest.js
74
+ - app/controllers/email_verifications_controller.rb
75
+ - app/controllers/emails_controller.rb
76
+ - app/controllers/password_resets_controller.rb
77
+ - app/controllers/passwords_controller.rb
78
+ - app/mailers/application_mailer.rb
79
+ - app/mailers/minimalist_authentication_mailer.rb
80
+ - app/views/email_verifications/new.html.erb
81
+ - app/views/email_verifications/show.html.erb
82
+ - app/views/emails/edit.html.erb
83
+ - app/views/layouts/mailer.html.erb
84
+ - app/views/layouts/mailer.text.erb
85
+ - app/views/minimalist_authentication_mailer/update_password.html.erb
86
+ - app/views/minimalist_authentication_mailer/update_password.text.erb
87
+ - app/views/minimalist_authentication_mailer/verify_email.html.erb
88
+ - app/views/minimalist_authentication_mailer/verify_email.text.erb
89
+ - app/views/password_resets/new.html.erb
90
+ - app/views/passwords/edit.html.erb
74
91
  - app/views/sessions/_form.html.erb
75
92
  - app/views/sessions/new.html.erb
93
+ - config/locales/minimalist_authentication.en.yml
76
94
  - config/routes.rb
77
95
  - lib/minimalist_authentication.rb
78
96
  - lib/minimalist_authentication/configuration.rb
79
97
  - lib/minimalist_authentication/controller.rb
80
98
  - lib/minimalist_authentication/conversions/merge_password_hash.rb
99
+ - lib/minimalist_authentication/email_verification.rb
81
100
  - lib/minimalist_authentication/engine.rb
82
101
  - lib/minimalist_authentication/null_password.rb
83
102
  - lib/minimalist_authentication/password.rb
84
103
  - lib/minimalist_authentication/sessions.rb
85
104
  - lib/minimalist_authentication/test_helper.rb
86
105
  - lib/minimalist_authentication/user.rb
106
+ - lib/minimalist_authentication/verifiable_token.rb
87
107
  - lib/minimalist_authentication/version.rb
88
108
  - lib/tasks/minimalist_authentication_tasks.rake
89
109
  homepage: https://github.com/wwidea/minimalist_authentication