minimalist_authentication 2.0.0 → 2.1.0

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