minimalist_authentication 3.0.0 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +49 -29
- data/app/controllers/email_verifications_controller.rb +5 -4
- data/app/controllers/emails_controller.rb +1 -1
- data/app/controllers/password_resets_controller.rb +21 -12
- data/app/controllers/passwords_controller.rb +13 -8
- data/app/helpers/minimalist_authentication/application_helper.rb +68 -0
- data/app/mailers/minimalist_authentication_mailer.rb +9 -13
- data/app/views/email_verifications/show.html.erb +1 -1
- data/app/views/emails/edit.html.erb +3 -3
- data/app/views/minimalist_authentication_mailer/update_password.html.erb +3 -1
- data/app/views/minimalist_authentication_mailer/update_password.text.erb +2 -2
- data/app/views/minimalist_authentication_mailer/verify_email.html.erb +9 -3
- data/app/views/minimalist_authentication_mailer/verify_email.text.erb +3 -3
- data/app/views/password_resets/new.html.erb +5 -6
- data/app/views/passwords/edit.html.erb +9 -7
- data/app/views/sessions/_form.html.erb +7 -7
- data/app/views/sessions/new.html.erb +3 -3
- data/config/locales/minimalist_authentication.en.yml +31 -9
- data/config/routes.rb +3 -7
- data/lib/minimalist_authentication/authenticator.rb +22 -15
- data/lib/minimalist_authentication/configuration.rb +10 -13
- data/lib/minimalist_authentication/controller.rb +3 -3
- data/lib/minimalist_authentication/email_verification.rb +29 -11
- data/lib/minimalist_authentication/engine.rb +5 -0
- data/lib/minimalist_authentication/sessions.rb +1 -1
- data/lib/minimalist_authentication/user.rb +54 -19
- data/lib/minimalist_authentication/verifiable_token.rb +21 -39
- data/lib/minimalist_authentication/version.rb +1 -1
- data/lib/minimalist_authentication.rb +10 -0
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba64bc8499dedd0f4fe8dfca813cc0c21e2d32759bd3b3b4dd8aac3c0e77605a
|
4
|
+
data.tar.gz: 566e4b373c274fd7843469d4d225560baf877aabde97658b1aa5a66f3c97f65e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 149c5f1eb8a13319ba9d4848a7851107433d96081ed9a1bf01e43cba0366cb1e873aaa47c10eefed29aba9b973bf53076ecefd6d7b6835bb1a9300a4db8813fc
|
7
|
+
data.tar.gz: 51aae04db4c7bcdf4a26009fc1278aa716aea12d95624fe74ffd21fb9d52e0c901ec78d15751df92c2ce2882544a54b80f623e72a53703e1a67b88072662aed9
|
data/README.md
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# MinimalistAuthentication
|
2
|
-
A Rails authentication gem that takes a minimalist approach. It is designed to be simple to understand, use, and
|
2
|
+
A Rails authentication gem that takes a minimalist approach. It is designed to be simple to understand, use, and customize for your application.
|
3
3
|
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
Add this line to your application's Gemfile:
|
7
7
|
|
8
8
|
```ruby
|
9
|
-
gem
|
9
|
+
gem "minimalist_authentication"
|
10
10
|
```
|
11
11
|
|
12
|
-
And then
|
12
|
+
And then run:
|
13
13
|
```bash
|
14
14
|
$ bundle
|
15
15
|
```
|
@@ -61,19 +61,29 @@ class ActiveSupport::TestCase
|
|
61
61
|
end
|
62
62
|
```
|
63
63
|
|
64
|
+
|
64
65
|
## Configuration
|
65
66
|
Customize the configuration with an initializer. Create a **minimalist_authentication.rb** file in config/initializers.
|
66
67
|
```ruby
|
67
68
|
MinimalistAuthentication.configure do |configuration|
|
68
|
-
configuration.
|
69
|
+
configuration.login_redirect_path = :custom_path # default is :root_path
|
70
|
+
configuration.logout_redirect_path = :custom_path # default is :new_session_path
|
71
|
+
configuration.request_email = true # default is true
|
69
72
|
configuration.session_key = :custom_session_key # default is :user_id
|
73
|
+
configuration.user_model_name = "CustomModelName" # default is "::User"
|
70
74
|
configuration.validate_email = true # default is true
|
71
75
|
configuration.validate_email_presence = true # default is true
|
72
|
-
configuration.request_email = true # default is true
|
73
76
|
configuration.verify_email = true # default is true
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
### Example with a Person Model
|
81
|
+
```ruby
|
82
|
+
MinimalistAuthentication.configure do |configuration|
|
83
|
+
configuration.login_redirect_path = :dashboard_path
|
84
|
+
configuration.session_key = :person_id
|
85
|
+
configuration.user_model_name = "Person"
|
86
|
+
configuration.validate_email_presence = false
|
77
87
|
end
|
78
88
|
```
|
79
89
|
|
@@ -87,38 +97,44 @@ example_user:
|
|
87
97
|
```
|
88
98
|
|
89
99
|
|
90
|
-
## Verification
|
91
|
-
|
92
|
-
module. Include the module in your user class and add the verification token columns
|
93
|
-
to the database.
|
94
|
-
|
95
|
-
Include MinimalistAuthentication::VerifiableToken in your user model (app/models/user.rb)
|
100
|
+
## Email Verification
|
101
|
+
Include MinimalistAuthentication::EmailVerification in your user model (app/models/user.rb)
|
96
102
|
```ruby
|
97
103
|
class User < ApplicationRecord
|
98
104
|
include MinimalistAuthentication::User
|
99
|
-
include MinimalistAuthentication::
|
105
|
+
include MinimalistAuthentication::EmailVerification
|
100
106
|
end
|
101
107
|
```
|
102
108
|
|
103
|
-
Add the **
|
104
|
-
Create a user model with **email** for an identifier:
|
109
|
+
Add the **email_verified_at** column to your user model:
|
105
110
|
```bash
|
106
|
-
bin/rails generate migration
|
111
|
+
bin/rails generate migration AddEmailVerifiedAtToUsers email_verified_at:datetime
|
107
112
|
```
|
108
113
|
|
109
|
-
|
110
|
-
|
114
|
+
|
115
|
+
## Verification Tokens
|
116
|
+
Verification token support is provided by the ```ActiveRecord::TokenFor#generate_token_for``` method. MinimalistAuthentication includes token definitions for **password_reset** and **email_verification**. These tokens are utilized by the **update_password** and **verify_email** email messages respectively, to allow users to update their passwords and verify their email addresses.
|
117
|
+
|
118
|
+
### Update Password
|
119
|
+
The **update_password** token expires in 1 hour and is invalidated when the user's password is changed.
|
120
|
+
|
121
|
+
#### Example
|
111
122
|
```ruby
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
end
|
123
|
+
token = user.generate_token_for(:password_reset)
|
124
|
+
User.find_by_token_for(:password_reset, token) # => user
|
125
|
+
user.update!(password: "new password")
|
126
|
+
User.find_by_token_for(:password_reset, token) # => nil
|
117
127
|
```
|
118
128
|
|
119
|
-
|
120
|
-
|
121
|
-
|
129
|
+
### Email Verification
|
130
|
+
The **email_verification** token expires in 1 hour and is invalidated when the user's email is changed.
|
131
|
+
|
132
|
+
#### Example
|
133
|
+
```ruby
|
134
|
+
token = user.generate_token_for(:email_verification)
|
135
|
+
User.find_by_token_for(:email_verification, token) # => user
|
136
|
+
user.update!(email: "new_email@example.com")
|
137
|
+
User.find_by_token_for(:email_verification, token) # => nil
|
122
138
|
```
|
123
139
|
|
124
140
|
|
@@ -127,7 +143,7 @@ bin/rails generate migration AddEmailVerifiedAtToUsers email_verified_at:datetim
|
|
127
143
|
### Upgrading to Version 2.0
|
128
144
|
Pre 2.0 versions of MinimalistAuthentication supported multiple hash algorithms
|
129
145
|
and stored the hashed password and salt as separate fields in the database
|
130
|
-
(crypted_password and salt). The
|
146
|
+
(crypted_password and salt). The 2.0 version of MinimalistAuthentication
|
131
147
|
uses BCrypt to hash passwords and stores the result in the **password_hash** field.
|
132
148
|
|
133
149
|
To convert from a pre 2.0 version add the **password_hash** to your user model
|
@@ -163,5 +179,9 @@ end
|
|
163
179
|
alias_attribute :password_digest, :password_hash
|
164
180
|
```
|
165
181
|
|
182
|
+
### Upgrading to Version 3.2
|
183
|
+
The **verification_token** and **verification_token_generated_at** database columns are no longer used and can be safely removed from your user model.
|
184
|
+
|
185
|
+
|
166
186
|
## License
|
167
187
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT)..
|
@@ -1,18 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class EmailVerificationsController < ApplicationController
|
4
|
+
# Verifies the email of the current_user using the provided token
|
4
5
|
def show
|
5
6
|
current_user.verify_email(params[:token])
|
6
7
|
end
|
7
8
|
|
9
|
+
# Form for current_user to request an email verification email
|
8
10
|
def new
|
9
|
-
#
|
11
|
+
# new.html.erb
|
10
12
|
end
|
11
13
|
|
14
|
+
# Sends an email verification email to the current_user
|
12
15
|
def create
|
13
|
-
current_user.
|
14
|
-
MinimalistAuthenticationMailer.verify_email(current_user).deliver_now
|
15
|
-
|
16
|
+
MinimalistAuthenticationMailer.with(user: current_user).verify_email.deliver_now
|
16
17
|
redirect_to dashboard_path, notice: t(".notice", email: current_user.email)
|
17
18
|
end
|
18
19
|
end
|
@@ -5,30 +5,39 @@ class PasswordResetsController < ApplicationController
|
|
5
5
|
|
6
6
|
layout "sessions"
|
7
7
|
|
8
|
-
#
|
8
|
+
# Renders form for user to request a password reset
|
9
9
|
def new
|
10
|
-
|
10
|
+
# new.html.erb
|
11
11
|
end
|
12
12
|
|
13
13
|
# Send a password update link to users with a verified email
|
14
14
|
def create
|
15
|
-
if
|
16
|
-
user
|
17
|
-
|
15
|
+
if email_valid?
|
16
|
+
send_update_password_email if user
|
17
|
+
|
18
|
+
# Always display notice to prevent leaking user emails
|
19
|
+
redirect_to new_session_path, notice: t(".notice", email:)
|
20
|
+
else
|
21
|
+
flash.now.alert = t(".alert")
|
22
|
+
render :new, status: :unprocessable_entity
|
18
23
|
end
|
19
|
-
# always display notice even if the user was not found to prevent leaking user emails
|
20
|
-
redirect_to new_session_path, notice: "Password reset instructions were mailed to #{email}"
|
21
24
|
end
|
22
25
|
|
23
26
|
private
|
24
27
|
|
25
|
-
def
|
26
|
-
|
28
|
+
def email
|
29
|
+
params.dig(:user, :email)
|
30
|
+
end
|
27
31
|
|
28
|
-
|
32
|
+
def send_update_password_email
|
33
|
+
MinimalistAuthenticationMailer.with(user:).update_password.deliver_now
|
29
34
|
end
|
30
35
|
|
31
|
-
def
|
32
|
-
|
36
|
+
def user
|
37
|
+
@user ||= MinimalistAuthentication.user_model.find_by_verified_email(email:)
|
38
|
+
end
|
39
|
+
|
40
|
+
def email_valid?
|
41
|
+
URI::MailTo::EMAIL_REGEXP.match?(email)
|
33
42
|
end
|
34
43
|
end
|
@@ -3,34 +3,39 @@
|
|
3
3
|
class PasswordsController < ApplicationController
|
4
4
|
skip_before_action :authorization_required
|
5
5
|
|
6
|
+
before_action :validate_token, only: %i[edit update]
|
7
|
+
|
6
8
|
layout "sessions"
|
7
9
|
|
8
10
|
# From for user to update password
|
9
11
|
def edit
|
10
|
-
|
11
|
-
# render passwords/edit.html.erb
|
12
|
+
# edit.html.erb
|
12
13
|
end
|
13
14
|
|
14
15
|
# Update user's password
|
15
16
|
def update
|
16
|
-
if user.
|
17
|
+
if user.update(password_params.merge(password_required: true))
|
17
18
|
redirect_to new_session_path, notice: t(".notice")
|
18
19
|
else
|
19
|
-
render :edit
|
20
|
+
render :edit, status: :unprocessable_entity
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
23
24
|
private
|
24
25
|
|
25
|
-
def
|
26
|
-
|
26
|
+
def password_params
|
27
|
+
params.require(:user).permit(:password, :password_confirmation)
|
27
28
|
end
|
28
29
|
|
29
30
|
def token
|
30
31
|
@token ||= params[:token]
|
31
32
|
end
|
32
33
|
|
33
|
-
def
|
34
|
-
|
34
|
+
def user
|
35
|
+
@user ||= MinimalistAuthentication.user_model.active.find_by_token_for(:password_reset, token)
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_token
|
39
|
+
redirect_to(new_session_path, alert: t(".invalid_token")) unless user
|
35
40
|
end
|
36
41
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MinimalistAuthentication
|
4
|
+
module ApplicationHelper
|
5
|
+
def ma_confirm_password_field(form, options = {})
|
6
|
+
form.password_field(
|
7
|
+
:password_confirmation,
|
8
|
+
options.reverse_merge(
|
9
|
+
autocomplete: "new-password",
|
10
|
+
minlength: MinimalistAuthentication.user_model.password_minimum,
|
11
|
+
placeholder: t(".password_confirmation.placeholder"),
|
12
|
+
required: true
|
13
|
+
)
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def ma_email_field(form, options = {})
|
18
|
+
form.email_field(
|
19
|
+
:email,
|
20
|
+
options.reverse_merge(
|
21
|
+
autocomplete: "email",
|
22
|
+
autofocus: true,
|
23
|
+
placeholder: t(".email.placeholder", default: true),
|
24
|
+
required: true
|
25
|
+
)
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def ma_forgot_password_link
|
30
|
+
link_to(t(".forgot_password"), new_password_reset_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def ma_new_password_field(form, options = {})
|
34
|
+
form.password_field(
|
35
|
+
:password,
|
36
|
+
options.reverse_merge(
|
37
|
+
autocomplete: "new-password",
|
38
|
+
minlength: MinimalistAuthentication.user_model.password_minimum,
|
39
|
+
placeholder: t(".password.placeholder"),
|
40
|
+
required: true
|
41
|
+
)
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
def ma_password_field(form, options = {})
|
46
|
+
form.password_field(
|
47
|
+
:password,
|
48
|
+
options.reverse_merge(
|
49
|
+
autocomplete: "current-password",
|
50
|
+
placeholder: true,
|
51
|
+
required: true
|
52
|
+
)
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def ma_username_field(form, options = {})
|
57
|
+
form.text_field(
|
58
|
+
:username,
|
59
|
+
options.reverse_merge(
|
60
|
+
autocomplete: "username",
|
61
|
+
autofocus: true,
|
62
|
+
placeholder: true,
|
63
|
+
required: true
|
64
|
+
)
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -1,24 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class MinimalistAuthenticationMailer < ApplicationMailer
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
before_action { @user = params[:user] }
|
5
|
+
after_action :mail_user
|
6
|
+
|
7
|
+
def verify_email
|
8
|
+
@verify_email_url = email_verification_url(token: @user.generate_token_for(:email_verification))
|
7
9
|
end
|
8
10
|
|
9
|
-
def update_password
|
10
|
-
@
|
11
|
-
send_to(user, "Update Password")
|
11
|
+
def update_password
|
12
|
+
@edit_password_url = edit_password_url(token: @user.generate_token_for(:password_reset))
|
12
13
|
end
|
13
14
|
|
14
15
|
private
|
15
16
|
|
16
|
-
def
|
17
|
-
@user
|
18
|
-
mail to: @user.email, subject: prefixed_subject(subject)
|
19
|
-
end
|
20
|
-
|
21
|
-
def prefixed_subject(subject)
|
22
|
-
"#{MinimalistAuthentication.configuration.email_prefix} #{subject}"
|
17
|
+
def mail_user
|
18
|
+
mail(to: @user.email)
|
23
19
|
end
|
24
20
|
end
|
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
<h2>Please update your email address</h2>
|
4
4
|
|
5
|
-
<%=
|
5
|
+
<%= form_with(model: current_user, url: email_path) do |form| %>
|
6
6
|
<%= form.text_field :email %>
|
7
|
-
<%= form.submit
|
7
|
+
<%= form.submit "Update" %>
|
8
8
|
<% end %>
|
9
9
|
|
10
10
|
<p>Providing your email will allow you to receive confidential messages and reset your password.</p>
|
11
11
|
|
12
|
-
<%= link_to(
|
12
|
+
<%= link_to("Skip Email Update", dashboard_path) %>
|
@@ -1,3 +1,3 @@
|
|
1
|
-
<%= t(
|
1
|
+
<%= t(".opening") %>
|
2
2
|
|
3
|
-
<%= @
|
3
|
+
<%= @edit_password_url %>
|
@@ -1,5 +1,11 @@
|
|
1
|
-
<p
|
1
|
+
<p>
|
2
|
+
<%= t(".opening") %>
|
3
|
+
</p>
|
2
4
|
|
3
|
-
|
5
|
+
<p>
|
6
|
+
<%= link_to("Verify Email Address", @verify_email_url, rel: "noopener noreferrer", target: "_blank") %>
|
7
|
+
</p>
|
4
8
|
|
5
|
-
<p
|
9
|
+
<p>
|
10
|
+
<%= t(".closing") %>
|
11
|
+
</p>
|
@@ -1,13 +1,12 @@
|
|
1
|
-
<h1
|
1
|
+
<h1><%= t(".title") %></h1>
|
2
2
|
|
3
3
|
<p>
|
4
|
-
|
4
|
+
<%= t(".instructions") %>
|
5
5
|
</p>
|
6
6
|
|
7
|
-
<%=
|
7
|
+
<%= form_with scope: :user, url: password_reset_path do |form| %>
|
8
8
|
<div>
|
9
|
-
<%= form
|
10
|
-
<%= form.text_field :email %>
|
9
|
+
<%= ma_email_field(form) %>
|
11
10
|
</div>
|
12
|
-
<%= form.submit
|
11
|
+
<%= form.submit t(".submit") %>
|
13
12
|
<% end %>
|
@@ -1,15 +1,17 @@
|
|
1
|
-
<h1
|
1
|
+
<h1><%= t(".title") %></h1>
|
2
|
+
|
3
|
+
<p><%= t(".instructions") %></p>
|
2
4
|
|
3
5
|
<%= @user.errors.full_messages if @user.errors.any? %>
|
4
6
|
|
5
|
-
<%=
|
7
|
+
<%= form_with model: @user, url: password_path(token: @token) do |form| %>
|
6
8
|
<div>
|
7
|
-
<%= form.label
|
8
|
-
<%= form
|
9
|
+
<%= form.label :password %>
|
10
|
+
<%= ma_new_password_field(form) %>
|
9
11
|
</div>
|
10
12
|
<div>
|
11
|
-
<%= form.label
|
12
|
-
<%= form
|
13
|
+
<%= form.label :password_confirmation %>
|
14
|
+
<%= ma_confirm_password_field(form) %>
|
13
15
|
</div>
|
14
|
-
<%= form.submit
|
16
|
+
<%= form.submit t(".submit") %>
|
15
17
|
<% end %>
|
@@ -1,11 +1,11 @@
|
|
1
|
-
<%=
|
1
|
+
<%= form_with model: @user, url: session_path do |form| %>
|
2
2
|
<div>
|
3
|
-
<%= form.label
|
4
|
-
<%= form
|
3
|
+
<%= form.label :email %>
|
4
|
+
<%= ma_email_field(form) %>
|
5
5
|
</div>
|
6
|
-
<div>
|
7
|
-
<%= form.label
|
8
|
-
<%= form
|
6
|
+
<div style="margin-top: 8px;">
|
7
|
+
<%= form.label :password %>
|
8
|
+
<%= ma_password_field(form) %>
|
9
9
|
</div>
|
10
|
-
<%= form.submit
|
10
|
+
<%= form.submit t("sessions.new.submit") %>
|
11
11
|
<% end %>
|
@@ -1,3 +1,3 @@
|
|
1
|
-
<h1
|
2
|
-
<%= render partial:
|
3
|
-
<%=
|
1
|
+
<h1><%= t(".title") %></h1>
|
2
|
+
<%= render partial: "form" %>
|
3
|
+
<%= ma_forgot_password_link %>
|
@@ -1,12 +1,38 @@
|
|
1
1
|
en:
|
2
|
-
# controllers
|
3
2
|
email_verifications:
|
4
3
|
create:
|
5
4
|
notice: Verification email sent to %{email}, follow the instructions to complete verification. Thank you!
|
6
5
|
emails:
|
7
6
|
update:
|
8
7
|
notice: Email successfully updated
|
8
|
+
minimalist_authentication_mailer:
|
9
|
+
update_password:
|
10
|
+
opening: Please click the link below to update your password.
|
11
|
+
subject: Update Password
|
12
|
+
verify_email:
|
13
|
+
closing: If you did not request email verification you can safely ignore this message.
|
14
|
+
opening: Please click the link below to complete your email verification.
|
15
|
+
subject: Email Address Verification
|
16
|
+
password_resets:
|
17
|
+
new:
|
18
|
+
email:
|
19
|
+
placeholder: Enter your email address
|
20
|
+
instructions: Enter your verified email address, and we'll send you a link to reset your password.
|
21
|
+
submit: Send Reset Link
|
22
|
+
title: Reset Your Password
|
23
|
+
create:
|
24
|
+
alert: Please provide a valid email address.
|
25
|
+
notice: We've sent password reset instructions to %{email}. If you don't receive the email within a few minutes, please check your spam folder.
|
9
26
|
passwords:
|
27
|
+
edit:
|
28
|
+
instructions: Please enter your new password.
|
29
|
+
password:
|
30
|
+
placeholder: New password
|
31
|
+
password_confirmation:
|
32
|
+
placeholder: Confirm password
|
33
|
+
submit: Reset Password
|
34
|
+
title: Reset Password
|
35
|
+
invalid_token: Your password reset link has expired. Please request a new one to continue.
|
10
36
|
update:
|
11
37
|
notice: Password successfully updated
|
12
38
|
sessions:
|
@@ -14,11 +40,7 @@ en:
|
|
14
40
|
alert: Couldn't log you in as %{identifier}
|
15
41
|
destroy:
|
16
42
|
notice: You have been logged out.
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
opening: Please click the link below to update your password.
|
22
|
-
verify_email:
|
23
|
-
opening: Please click the link below to complete your email verification.
|
24
|
-
closing: If you did not request email verification you can safely ignore this message.
|
43
|
+
new:
|
44
|
+
forgot_password: Forgot password?
|
45
|
+
title: Sign In
|
46
|
+
submit: Sign In
|
data/config/routes.rb
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
Rails.application.routes.draw do
|
4
|
-
|
5
|
-
resource :password, only: %i[edit update]
|
6
|
-
end
|
7
|
-
|
8
|
-
resource :password_reset, only: %i[new create]
|
9
|
-
|
4
|
+
resource :email_verification, only: %i[show new create]
|
10
5
|
resource :email, only: %i[edit update]
|
11
|
-
resource :
|
6
|
+
resource :password_reset, only: %i[new create]
|
7
|
+
resource :password, only: %i[edit update]
|
12
8
|
end
|
@@ -13,37 +13,44 @@ module MinimalistAuthentication
|
|
13
13
|
# Params examples:
|
14
14
|
# { email: 'user@example.com', password: 'abc123' }
|
15
15
|
# { username: 'user', password: 'abc123' }
|
16
|
-
|
17
|
-
def self.authenticated_user(params)
|
16
|
+
def self.authenticate(params)
|
18
17
|
hash = params.to_h.with_indifferent_access
|
18
|
+
field, value = hash.find { |key, _| LOGIN_FIELDS.include?(key) }
|
19
|
+
new(field:, value:, password: hash["password"]).authenticated_user
|
20
|
+
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
def self.authenticated_user(params)
|
23
|
+
MinimalistAuthentication.deprecator.warn(<<-MSG.squish)
|
24
|
+
Calling MinimalistAuthentication::Authenticator.authenticated_user is deprecated.
|
25
|
+
Use MinimalistAuthentication::Authenticator.authenticate instead.
|
26
|
+
MSG
|
27
|
+
authenticate(params)
|
25
28
|
end
|
26
29
|
|
30
|
+
# Initializes a new Authenticator instance with the provided field, value, and password.
|
27
31
|
def initialize(field:, value:, password:)
|
28
32
|
@field = field
|
29
33
|
@value = value
|
30
34
|
@password = password
|
31
35
|
end
|
32
36
|
|
33
|
-
# Returns
|
37
|
+
# Returns an authenticated and enabled user or nil.
|
34
38
|
def authenticated_user
|
35
|
-
|
39
|
+
authenticated&.enabled if valid?
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Attempts to authenticate a user based on the provided field, value, and password.
|
45
|
+
# Returns user upon successful authentication, otherwise returns nil.
|
46
|
+
def authenticated
|
47
|
+
MinimalistAuthentication.configuration.user_model.authenticate_by(field => value, password:)
|
36
48
|
end
|
37
49
|
|
38
50
|
# Returns true if all the authentication attributes are present.
|
51
|
+
# Otherwise returns false.
|
39
52
|
def valid?
|
40
53
|
[field, value, password].all?(&:present?)
|
41
54
|
end
|
42
|
-
|
43
|
-
private
|
44
|
-
|
45
|
-
def user
|
46
|
-
@user ||= MinimalistAuthentication.configuration.user_model.active.find_by(field => value)
|
47
|
-
end
|
48
55
|
end
|
49
56
|
end
|
@@ -51,10 +51,6 @@ module MinimalistAuthentication
|
|
51
51
|
# Defaults to :new_session_path
|
52
52
|
attr_accessor :logout_redirect_path
|
53
53
|
|
54
|
-
# Email subject prefix for MinimalistAuthenticationMailer messages
|
55
|
-
# Defaults to application name
|
56
|
-
attr_accessor :email_prefix
|
57
|
-
|
58
54
|
def initialize
|
59
55
|
self.user_model_name = "::User"
|
60
56
|
self.session_key = :user_id
|
@@ -64,20 +60,21 @@ module MinimalistAuthentication
|
|
64
60
|
self.verify_email = true
|
65
61
|
self.login_redirect_path = :root_path
|
66
62
|
self.logout_redirect_path = :new_session_path
|
67
|
-
self.email_prefix = default_email_prefix
|
68
63
|
end
|
69
64
|
|
70
|
-
#
|
71
|
-
|
72
|
-
|
73
|
-
def user_model
|
74
|
-
user_model_name.constantize
|
65
|
+
# Clear the user_model class
|
66
|
+
def clear_user_model
|
67
|
+
@user_model = nil
|
75
68
|
end
|
76
69
|
|
77
|
-
|
70
|
+
# Display deprecation warning for email_prefix
|
71
|
+
def email_prefix=(_)
|
72
|
+
MinimalistAuthentication.deprecator.warn("The #email_prefix configuration setting is no longer supported.")
|
73
|
+
end
|
78
74
|
|
79
|
-
|
80
|
-
|
75
|
+
# Returns the user_model class
|
76
|
+
def user_model
|
77
|
+
@user_model ||= user_model_name.constantize
|
81
78
|
end
|
82
79
|
end
|
83
80
|
end
|
@@ -9,6 +9,8 @@ module MinimalistAuthentication
|
|
9
9
|
# use skip_before_action to open up specific actions
|
10
10
|
before_action :authorization_required
|
11
11
|
|
12
|
+
helper MinimalistAuthentication::ApplicationHelper
|
13
|
+
|
12
14
|
helper_method :current_user, :logged_in?, :authorized?
|
13
15
|
end
|
14
16
|
|
@@ -19,9 +21,7 @@ module MinimalistAuthentication
|
|
19
21
|
end
|
20
22
|
|
21
23
|
def find_session_user
|
22
|
-
|
23
|
-
|
24
|
-
MinimalistAuthentication.configuration.user_model.active.find_by(id: session_user_id)
|
24
|
+
MinimalistAuthentication.configuration.user_model.find_enabled(session_user_id)
|
25
25
|
end
|
26
26
|
|
27
27
|
def session_user_id
|
@@ -5,9 +5,31 @@ module MinimalistAuthentication
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
8
|
-
|
8
|
+
generates_token_for :email_verification, expires_in: 1.hour do
|
9
|
+
email
|
10
|
+
end
|
9
11
|
|
10
|
-
|
12
|
+
before_save :clear_email_verification, if: :email_changed?
|
13
|
+
|
14
|
+
scope :with_verified_email, -> { where.not(email_verified_at: nil) }
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def email_verified
|
19
|
+
MinimalistAuthentication.deprecator.warn(<<-MSG.squish)
|
20
|
+
Calling #email_verified is deprecated.
|
21
|
+
Call #with_verified_email instead.
|
22
|
+
MSG
|
23
|
+
with_verified_email
|
24
|
+
end
|
25
|
+
|
26
|
+
def find_by_verified_email(email:)
|
27
|
+
active.with_verified_email.find_by(email:)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def email_verified?
|
32
|
+
email.present? && email_verified_at.present?
|
11
33
|
end
|
12
34
|
|
13
35
|
def needs_email_set?
|
@@ -18,26 +40,22 @@ module MinimalistAuthentication
|
|
18
40
|
email_verification_enabled? && email.present? && email_verified_at.blank?
|
19
41
|
end
|
20
42
|
|
21
|
-
def email_verified?
|
22
|
-
email.present? && email_verified_at.present?
|
23
|
-
end
|
24
|
-
|
25
43
|
def verify_email(token)
|
26
|
-
|
44
|
+
touch(:email_verified_at) if token_owner?(:email_verification, token)
|
27
45
|
end
|
28
46
|
|
29
47
|
private
|
30
48
|
|
31
|
-
def
|
32
|
-
|
49
|
+
def clear_email_verification
|
50
|
+
self.email_verified_at = nil
|
33
51
|
end
|
34
52
|
|
35
53
|
def email_verification_enabled?
|
36
54
|
MinimalistAuthentication.configuration.verify_email
|
37
55
|
end
|
38
56
|
|
39
|
-
def
|
40
|
-
|
57
|
+
def request_email_enabled?
|
58
|
+
MinimalistAuthentication.configuration.request_email
|
41
59
|
end
|
42
60
|
end
|
43
61
|
end
|
@@ -37,7 +37,7 @@ module MinimalistAuthentication
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def authenticated_user
|
40
|
-
@authenticated_user ||= MinimalistAuthentication::Authenticator.
|
40
|
+
@authenticated_user ||= MinimalistAuthentication::Authenticator.authenticate(user_params)
|
41
41
|
end
|
42
42
|
|
43
43
|
def log_in_user
|
@@ -9,7 +9,11 @@ module MinimalistAuthentication
|
|
9
9
|
GUEST_USER_EMAIL = "guest"
|
10
10
|
|
11
11
|
included do
|
12
|
-
has_secure_password
|
12
|
+
has_secure_password
|
13
|
+
|
14
|
+
generates_token_for :password_reset, expires_in: 1.hour do
|
15
|
+
password_salt.last(10)
|
16
|
+
end
|
13
17
|
|
14
18
|
# Force validations for a blank password.
|
15
19
|
attribute :password_required, :boolean, default: false
|
@@ -24,37 +28,69 @@ module MinimalistAuthentication
|
|
24
28
|
validates(:email, presence: true, if: :validate_email_presence?)
|
25
29
|
|
26
30
|
# Password validations
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
presence: true,
|
32
|
-
if: :validate_password?
|
33
|
-
)
|
31
|
+
# Adds validations for minimum password length and exclusivity.
|
32
|
+
# has_secure_password adds validations for presence, maximum length, confirmation,
|
33
|
+
# and password_challenge.
|
34
|
+
validates :password, length: { minimum: :password_minimum }, if: :validate_password?
|
34
35
|
validate :password_exclusivity, if: :password?
|
35
36
|
|
36
37
|
# Active scope
|
37
|
-
scope :active,
|
38
|
-
|
38
|
+
scope :active, ->(state = true) { where(active: state) }
|
39
|
+
|
40
|
+
delegate :password_minimum, to: :class
|
39
41
|
end
|
40
42
|
|
41
43
|
module ClassMethods
|
44
|
+
# Finds a user by their id and returns the user if they are enabled.
|
45
|
+
# Returns nil if the user is not found or not enabled.
|
46
|
+
def find_enabled(id)
|
47
|
+
find_by(id:)&.enabled if id.present?
|
48
|
+
end
|
49
|
+
|
50
|
+
def inactive
|
51
|
+
MinimalistAuthentication.deprecator.warn(<<-MSG.squish)
|
52
|
+
Calling #inactive is deprecated. Use #active(false) instead.
|
53
|
+
MSG
|
54
|
+
active(false)
|
55
|
+
end
|
56
|
+
|
42
57
|
# Returns a frozen user with the email set to GUEST_USER_EMAIL.
|
43
58
|
def guest
|
44
59
|
new(email: GUEST_USER_EMAIL).freeze
|
45
60
|
end
|
61
|
+
|
62
|
+
# Minimum password length
|
63
|
+
def password_minimum = 12
|
64
|
+
end
|
65
|
+
|
66
|
+
# Called after a user is authenticated to determine if the user object should be returned.
|
67
|
+
def enabled
|
68
|
+
self if enabled?
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns true if the user is enabled.
|
72
|
+
# Override this method in your user model to implement custom logic that determines if a user is eligible to log in.
|
73
|
+
def enabled?
|
74
|
+
active?
|
75
|
+
end
|
76
|
+
|
77
|
+
# Remove the has_secure_password password blank error if password is not required.
|
78
|
+
def errors
|
79
|
+
super.tap { |errors| errors.delete(:password, :blank) unless validate_password? }
|
46
80
|
end
|
47
81
|
|
48
82
|
# Returns true if the user is not active.
|
49
83
|
def inactive?
|
84
|
+
MinimalistAuthentication.deprecator.warn("Calling #inactive? is deprecated.")
|
50
85
|
!active?
|
51
86
|
end
|
52
87
|
|
53
88
|
# Returns true if password matches the hashed_password, otherwise returns false.
|
54
89
|
def authenticated?(password)
|
90
|
+
MinimalistAuthentication.deprecator.warn(<<-MSG.squish)
|
91
|
+
Calling #authenticated? is deprecated. Use #authenticate instead.
|
92
|
+
MSG
|
55
93
|
authenticate(password)
|
56
|
-
rescue ::BCrypt::Errors::InvalidHash
|
57
|
-
false
|
58
94
|
end
|
59
95
|
|
60
96
|
# Check if user is a guest based on their email attribute
|
@@ -62,17 +98,11 @@ module MinimalistAuthentication
|
|
62
98
|
email == GUEST_USER_EMAIL
|
63
99
|
end
|
64
100
|
|
101
|
+
# Sets #last_logged_in_at to the current time without updating the updated_at timestamp.
|
65
102
|
def logged_in
|
66
|
-
# Use update_column to avoid updated_on trigger
|
67
103
|
update_column(:last_logged_in_at, Time.current)
|
68
104
|
end
|
69
105
|
|
70
|
-
# Minimum password length
|
71
|
-
def password_minimum = 12
|
72
|
-
|
73
|
-
# Maximum password length
|
74
|
-
def password_maximum = 40
|
75
|
-
|
76
106
|
# Checks for password presence
|
77
107
|
def password?
|
78
108
|
password.present?
|
@@ -87,6 +117,11 @@ module MinimalistAuthentication
|
|
87
117
|
end
|
88
118
|
end
|
89
119
|
|
120
|
+
# Return true if the user matches the owner of the provided token.
|
121
|
+
def token_owner?(purpose, token)
|
122
|
+
self.class.find_by_token_for(purpose, token) == self
|
123
|
+
end
|
124
|
+
|
90
125
|
# Require password for active users that either do no have a password hash
|
91
126
|
# stored OR are attempting to set a new password. Set **password_required**
|
92
127
|
# to true to force validations even when the password field is blank.
|
@@ -4,51 +4,33 @@ module MinimalistAuthentication
|
|
4
4
|
module VerifiableToken
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
def secure_update(token, attributes)
|
15
|
-
if matches_verification_token?(token)
|
16
|
-
update(attributes) && clear_token
|
17
|
-
else
|
18
|
-
errors.add(:base, "Verification token check failed")
|
19
|
-
false
|
20
|
-
end
|
7
|
+
included do
|
8
|
+
MinimalistAuthentication.deprecator.warn(<<-MSG.squish)
|
9
|
+
MinimalistAuthentication::VerifiableToken is no longer required.
|
10
|
+
You can safely remove the include from your user model.
|
11
|
+
MSG
|
21
12
|
end
|
22
13
|
|
23
|
-
def matches_verification_token?(
|
24
|
-
|
14
|
+
def matches_verification_token?(_token)
|
15
|
+
MinimalistAuthentication.deprecator.warn(<<-MSG.squish)
|
16
|
+
Calling #matches_verification_token? is deprecated.
|
17
|
+
MSG
|
25
18
|
end
|
26
19
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def clear_token
|
36
|
-
update_token(token: nil, time: nil)
|
37
|
-
end
|
38
|
-
|
39
|
-
def update_token(token: self.class.generate_unique_secure_token, time: Time.now.utc)
|
40
|
-
update!(
|
41
|
-
verification_token: token,
|
42
|
-
verification_token_generated_at: time
|
43
|
-
)
|
20
|
+
def regenerate_verification_token
|
21
|
+
MinimalistAuthentication.deprecator.warn(<<-MSG.squish)
|
22
|
+
Calling #regenerate_verification_token is deprecated and no longer generates tokens.
|
23
|
+
Call #generate_token_for with an argument of :password_reset or
|
24
|
+
:email_verification instead.
|
25
|
+
MSG
|
44
26
|
end
|
45
27
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
28
|
+
def verification_token
|
29
|
+
MinimalistAuthentication.deprecator.warn(<<-MSG.squish)
|
30
|
+
Calling #verification_token is deprecated and no longer returns a valid token.
|
31
|
+
Call #generate_token_for with an argument of :password_reset or
|
32
|
+
:email_verification instead.
|
33
|
+
MSG
|
52
34
|
end
|
53
35
|
end
|
54
36
|
end
|
@@ -9,3 +9,13 @@ require "minimalist_authentication/email_verification"
|
|
9
9
|
require "minimalist_authentication/controller"
|
10
10
|
require "minimalist_authentication/sessions"
|
11
11
|
require "minimalist_authentication/test_helper"
|
12
|
+
|
13
|
+
module MinimalistAuthentication
|
14
|
+
class << self
|
15
|
+
delegate :user_model, to: :configuration
|
16
|
+
|
17
|
+
def deprecator
|
18
|
+
@deprecator ||= ActiveSupport::Deprecation.new("4.0", name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
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: 3.
|
4
|
+
version: 3.2.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: 2024-11-
|
12
|
+
date: 2024-11-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bcrypt
|
@@ -37,14 +37,14 @@ dependencies:
|
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 7.1.0
|
41
41
|
type: :runtime
|
42
42
|
prerelease: false
|
43
43
|
version_requirements: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 7.1.0
|
48
48
|
description: A Rails authentication plugin that takes a minimalist approach. It is
|
49
49
|
designed to be simple to understand, use, and modify for your application.
|
50
50
|
email:
|
@@ -60,6 +60,7 @@ files:
|
|
60
60
|
- app/controllers/emails_controller.rb
|
61
61
|
- app/controllers/password_resets_controller.rb
|
62
62
|
- app/controllers/passwords_controller.rb
|
63
|
+
- app/helpers/minimalist_authentication/application_helper.rb
|
63
64
|
- app/mailers/application_mailer.rb
|
64
65
|
- app/mailers/minimalist_authentication_mailer.rb
|
65
66
|
- app/views/email_verifications/new.html.erb
|
@@ -94,7 +95,6 @@ licenses:
|
|
94
95
|
- MIT
|
95
96
|
metadata:
|
96
97
|
homepage_uri: https://github.com/wwidea/minimalist_authentication
|
97
|
-
source_code_uri: https://github.com/wwidea/minimalist_authentication
|
98
98
|
rubygems_mfa_required: 'true'
|
99
99
|
post_install_message:
|
100
100
|
rdoc_options: []
|