Skip to content

Commit fb7544b

Browse files
authored
Add password reset to authentication generator (rails#52472)
* Add password reset flow * Stray CR * Match link title * Simplify the HTML * No need to repeat * Fix title * No need for this CR * Accidentally removed condition * Fix token references need to be symbols * Use new automated token generator * Use new built-in password reset token and slim down controller * Add preview and reset optionality * Fix preview path and reset opt-out * Simplify the HTML * Fix problem with routes * No need for password resets to be optional Keep it simple
1 parent e5379fc commit fb7544b

File tree

12 files changed

+93
-7
lines changed

12 files changed

+93
-7
lines changed

railties/lib/rails/generators/erb/authentication/authentication_generator.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ module Erb # :nodoc:
66
module Generators # :nodoc:
77
class AuthenticationGenerator < Rails::Generators::Base # :nodoc:
88
def create_files
9+
template "views/passwords/new.html.erb", File.join("app/views/passwords/new.html.erb")
10+
template "views/passwords/edit.html.erb", File.join("app/views/passwords/edit.html.erb")
911
template "views/sessions/new.html.erb", File.join("app/views/sessions/new.html.erb")
1012
end
1113
end
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<h1>Update your password</h2>
2+
3+
<%%= tag.div(flash[:alert], style: "color:red") if flash[:alert] %>
4+
5+
<%%= form_with url: password_path(params[:token]), method: :put do |form| %>
6+
<%%= form.password_field :password, required: true, autocomplete: "off", placeholder: "Enter new password", maxlength: 72 %><br>
7+
<%%= form.password_field :password_confirmation, required: true, autocomplete: "off", placeholder: "Repeat new password", maxlength: 72 %><br>
8+
<%%= form.submit "Save" %>
9+
<%% end %>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<h1>Forgot your password?</h1>
2+
3+
<%%= tag.div(flash[:alert], style: "color:red") if flash[:alert] %>
4+
5+
<%%= form_with url: passwords_path do |form| %>
6+
<%%= form.email_field :email_address, required: true, autofocus: true, autocomplete: "username", placeholder: "Enter your email address", value: params[:email_address] %><br>
7+
<%%= form.submit "Email reset instructions" %>
8+
<%% end %>

railties/lib/rails/generators/erb/authentication/templates/views/sessions/new.html.erb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
<%% if alert = flash[:alert] %>
2-
<div style="color:red"><%%= alert %></div>
3-
<%% end %>
1+
<%%= tag.div(flash[:alert], style: "color:red") if flash[:alert] %>
2+
<%%= tag.div(flash[:notice], style: "color:green") if flash[:notice] %>
43

54
<%%= form_with url: session_path do |form| %>
65
<div>
@@ -13,3 +12,6 @@
1312

1413
<%%= form.submit "Sign in" %>
1514
<%% end %>
15+
<br>
16+
17+
<%%= link_to "Forgot password?", new_password_path %>

railties/lib/rails/generators/rails/authentication/USAGE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Description:
2-
Generates a basic authentication system with users and sessions.
2+
Generates a basic authentication system with users, sessions, and password reset.
33

44
Example:
55
`bin/rails generate authentication`

railties/lib/rails/generators/rails/authentication/authentication_generator.rb

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,29 @@ class AuthenticationGenerator < Base # :nodoc:
1010
invoke template_engine unless options.api?
1111
end
1212

13-
def create_files
13+
def create_authentication_files
1414
template "models/session.rb", File.join("app/models/session.rb")
1515
template "models/user.rb", File.join("app/models/user.rb")
1616
template "models/current.rb", File.join("app/models/current.rb")
1717

1818
template "controllers/sessions_controller.rb", File.join("app/controllers/sessions_controller.rb")
1919
template "controllers/concerns/authentication.rb", File.join("app/controllers/concerns/authentication.rb")
20+
template "controllers/passwords_controller.rb", File.join("app/controllers/passwords_controller.rb")
21+
22+
template "mailers/passwords_mailer.rb", File.join("app/mailers/passwords_mailer.rb")
23+
24+
template "views/passwords_mailer/reset.html.erb", File.join("app/views/passwords_mailer/reset.html.erb")
25+
template "views/passwords_mailer/reset.text.erb", File.join("app/views/passwords_mailer/reset.text.erb")
26+
27+
template "test/mailers/previews/passwords_mailer_preview.rb", File.join("test/mailers/previews/passwords_mailer_preview.rb")
2028
end
2129

22-
def configure_application
30+
def configure_application_controller
2331
gsub_file "app/controllers/application_controller.rb", /(class ApplicationController < ActionController::Base)/, "\\1\n include Authentication"
32+
end
33+
34+
def configure_authentication_routes
35+
route "resources :passwords, param: :token"
2436
route "resource :session"
2537
end
2638

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
class PasswordsController < ApplicationController
2+
allow_unauthenticated_access
3+
before_action :set_user_by_token, only: %i[ edit update ]
4+
5+
def new
6+
end
7+
8+
def create
9+
if user = User.find_by(email_address: params[:email_address])
10+
PasswordsMailer.reset(user).deliver_later
11+
end
12+
13+
redirect_to new_session_url, notice: "Password reset instructions sent (if user with that email address exists)."
14+
end
15+
16+
def edit
17+
end
18+
19+
def update
20+
if @user.update(params.permit(:password, :password_confirmation))
21+
redirect_to new_session_url, notice: "Password has been reset."
22+
else
23+
redirect_to edit_password_url(params[:token]), alert: "Passwords did not match."
24+
end
25+
end
26+
27+
private
28+
def set_user_by_token
29+
@user = User.find_by_password_reset_token!(params[:token])
30+
rescue ActiveSupport::MessageVerifier::InvalidSignature
31+
redirect_to new_password_url, alert: "Password reset link is invalid or has expired."
32+
end
33+
end
34+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class PasswordsMailer < ApplicationMailer
2+
def reset(user)
3+
@user = user
4+
mail subject: "Reset your password", to: user.email_address, from: "[email protected]"
5+
end
6+
end
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
class User < ApplicationRecord
2-
has_secure_password validations: false
2+
has_secure_password
33
has_many :sessions, dependent: :destroy
44
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Preview all emails at http://localhost:3000/rails/mailers/passwords_mailer
2+
class PasswordsMailerPreview < ActionMailer::Preview
3+
# Preview this email at http://localhost:3000/rails/mailers/passwords_mailer/reset
4+
def reset
5+
PasswordsMailer.reset(User.take)
6+
end
7+
end

0 commit comments

Comments
 (0)