customize forget password page devise ruby - ruby-on-rails

I have an application. I'm using ruby api as back end for the app. I'm using react native as front end. I'm using devise for authentication. We currently have the devise standard forget password page. I want to customize the page and make the page looks better. after my new implementation i get the email and when i click on the forget password link a blank tab opens up instead of the new page.
below is the current page.
I created view new.html.erb file in the below path
app/views/devise/passwords/new.html.erb
<h2>Change your passwordTest</h2>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %>
<%= render "devise/shared/error_messages", resource: resource %>
<%= f.hidden_field :reset_password_token %>
<div class="field">
<%= f.label :password, "New password" %><br />
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em><br />
<% end %>
<%= f.password_field :password, autofocus: true, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation, "Confirm new password" %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="actions">
<%= f.submit "Change my password" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
in the route.rb file, i have the below code
devise_for :users, controllers: { registrations: 'users/registrations', sessions: 'users/sessions', invitations: 'users/invitations', confirmations: 'users/confirmations', omniauth_callbacks: 'users/omniauth_callbacks', passwords: 'users/passwords'}
here is my passwords_controller.rb
app/controllers/users/passwords_controller.rb
class Users::PasswordsController < Devise::PasswordsController
end
here is the development.log logs
Started GET "/users/password/edit?reset_password_token=[FILTERED]" for ::1 at 2021-02-25 21:29:36 -0700
Processing by Users::PasswordsController#edit as HTML
Parameters: {"reset_password_token"=>"[FILTERED]"}
Completed 204 No Content in 1ms (ActiveRecord: 0.0ms)

The link you clicking is:
http://localhost:3000/users/password/edit?reset_password_token=xz_gdihd8NBvLp36xD-U
this redirects you to the edit method of Users::PasswordsController
the edit method by default refers the edit.html.erb instead of new.html.erb view. So you just need to customize this view.
You can check how this view is implemented on the devise github page: https://github.com/heartcombo/devise/blob/master/app/views/devise/passwords/edit.html.erb just remember to include the reset_password_token that is being passed in the query params

Related

How to signup in two different pages with devise

I used devise inthe login system.I want create two new html under the registrations, which is two different roles' pages. And I want to go to that page by choosing roles in registrations/new, which is the original signup file. What should I do next?
my files order in the view/user/registrations: enter image description here.
edit.html.erb
new.html.erb
new_librarian.html.erb
new_student.html.erb
I want to signup in the new two pages through registrations/new
<!-- what i changed in the registrations/new -->
<h2>Sign up</h2>
<p>Sign up as a Student<%= link_to 'Student', users_registrations_new_student_path, :method => 'get' %></p>
<p>Sign up as a Librarian<%= link_to 'Librarian', users_registrations_new_librarian_path, :method => 'get'%></p>
<!-- what is in the registrations/new_student -->
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= render "devise/shared/error_messages", adminv: resource %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "new-password" %>
</div>
<div class="field">
<%= f.label :select_role %><br />
<%= f.collection_select :role, User::ROLES, :to_s, :humanize %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
<!-- what i changed in the routes.rb -->
get 'users/registrations/new_librarian'
get 'users/registrations/new_student'
Could not find devise mapping for path "/users/registrations/new_student". This may happen for two reasons:
1) You forgot to wrap your route inside the scope block. For example: devise_scope :user do get "/some/route" => "some_devise_controller" end
2) You are testing a Devise controller bypassing the router. If so, you can explicitly tell Devise which mapping to use: #request.env["devise.mapping"] = Devise.mappings[:user]
You can achieve this by overriding the devise registration controller. In your overrided registration controller, in the new method once the user is saved,
#app/config/routes.rb
devise_for :users, :controllers => {:registrations => "registrations"}
# app/controllers/registrations_controller.rb
class RegistrationsController < Devise::RegistrationsController
def new
if #user.save!
if #user.role == "librarian"
redirect_to librarian_profile_path
elsif #user.role == "student"
redirect_to student_profile_path
end
end
end
end
You can define librarian_profile_path and student_profile_path in your routes
#app/config/routes.rb
get 'librarian/new' => 'librarian_profile#new', :as => 'librarian_profile'
get 'student/new' => 'student_profile#new', :as => 'student_profile'

reading parameters from create method

I have a ruby on rails project that is using the devise gem for authentication. I'm trying to use a captcha form in the sign up page to prevent bots from creating thousands of dummy logins
I'm not interested in using the recaptcha gem that works with devise as I haven't had any luck setting it up correctly.
I trying to set it up using this as a reference https://recaptchainrubyonrails.blogspot.com/
Here's my sign up page
<h2>Sign up</h2>
<%= form_for(resource, as: resource_name, url:
registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email" %>
</div>
<div class="field">
<%= f.label :password %>
<% if #minimum_password_length %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<div class="g-recaptcha" data
sitekey="6LeeL5gUAAAAADbq0vt4d9biAs8oegkPx9ttUkKb"></div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<br>
I do have my routes file point to a separate registrations controller instead of the one created within the devise registrations
devise_for :users, path: '', path_names: { sign_in: 'login', sign_out: 'logout' }, :controllers => {:registrations => "registrations"}
I can create a user regardless if I click on the captcha form. When I do click on it I get the parameter, g-recaptcha-response, in the create method that has a long text string such as
"g-recaptcha-response"=>"1234567890abcdefghi..."
I tried a create method that would look at that parameter and see if it is not nil. When it is nil I would like to back one page to the sign up page.
class RegistrationsController < Devise::RegistrationsController
def create
if not params[:g-recaptcha-response].present?
redirect_to :back
end
end
end
I can't even get to the parameter as it errors out saying
undefined local variable or method `recaptcha' for #<RegistrationsController:0x7219e5a0> Did you mean? catch"
I'm trying to capture this parameter, look at the value of g-recaptcha-response and somehow cancel the creation of a user if the response was nil or empty string
Alrighty, this is what I figured out that worked. Thanks to everyone who helped
#response = params[:"g-recaptcha-response"]
if #response == nil
flash[:notice] = "Please click on captcha form!"
redirect_back(fallback_location: new_user_registration_path)
else
flash[:notice] = "You signed up successfully!"
redirect_to root_path
end
Use 'g-recaptcha-response' instead of :g-recaptcha-response. You need to use string syntax because you cannot use - in symbol naming

Error disabling sign-ups for Devise model - Devise | Rails 5.2

I'm working on a Rails 5.2 project with two Devise models (Admin and User). I've followed the suggestion on this Stack Overflow answer to disable sign-ups for a Devise model without breaking Devise's edit_admin_registration_path.
After applying the changes suggested in routes.rb, running rails routes shows the changes working as expected, however when going to edit an Admin (edit_admin_registration_path), i get the error:
NoMethodError in Admins::RegistrationsController#edit
undefined method `validatable?' for nil:NilClass
I suspect the issue is that the linked answer was not using multiple Devise models with scoped views and controllers.
Any ideas?
I've added this to routes.rb:
devise_scope :admin do
get "/sign_in" => "admins/sessions#new" # custom path to login/sign_in
get "/sign_up" => "admins/registrations#new", as: "new_admin_registration" # custom path to sign_up/registration
end
devise_for :admins, skip: [ :registrations ], path: 'admins', controllers: { sessions: 'admins/sessions', registrations: 'admins/registrations' }
as :admins do
get 'admins/edit' => 'admins/registrations#edit', as: 'edit_admin_registration'
put 'admins' => 'admins/registrations#update', as: 'admin_registration'
end
Form on view:
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), method: :put) do |f| %>
<%= devise_error_messages! %>
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true, autocomplete: "email", class: "form-control" %>
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
<%= f.password_field :password, autocomplete: "off", class: "form-control" %>
<% if #minimum_password_length %>
<em><%= #minimum_password_length %> characters minimum</em>
<% end %>
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off", class: "form-control" %>
<%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
<%= f.password_field :current_password, autocomplete: "off", class: "form-control" %>
<%= f.submit "Update", class: "btn btn-success" %>
<% end %>
Answer found:
Routes needed to be configured like so (as can be found in this Stack Overflow answer):
devise_for :admins, :skip => [:registrations], path: 'admins', controllers: { sessions: 'admins/sessions', registrations: 'admins/registrations' }
devise_scope :admin do
get 'admins/edit' => 'admins/registrations#edit', as: 'edit_admin_registration'
put 'admins' => 'admins/registrations#update', as: 'admin_registration'
end

RoR: Forgot Password & Sign In - in same page

I'm trying to put Forgot password field with the sign in page, but if user is not registered (and not in apps database), then it redirects to the original devise Forgot password page with errors (http://localhost:3000/users/password). How do I make the errors appear in the same page as the sign in page (http://localhost:3000/users/sign_in)?
In app/views/devise/sessions/new.html.erb file
<%= simple_form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<%= f.email_field :email, required: false, autofocus: true, placeholder: "Username" %>
<%= f.password_field :password, required: false, placeholder: "Password" %>
<%= f.button :submit, "Sign In", class: "btn btn-success btn-sm" %>
<div class="remember-forgot">
<div class="row">
<div class="col-md-6">
<%= f.input :remember_me, as: :boolean if devise_mapping.rememberable? %>
</div>
<div class="col-md-6 forgot-pass-content">
Forgot Password
</div>
</div>
</div>
<% end %>
<!-- where reset is -->
<div class="pass-reset">
<%= simple_form_for(resource, as: resource_name, url: password_path(resource_name), namespace: 'forgot', html: { method: :post }) do |f| %>
<%= f.error_notification %>
<label>Enter the email you signed up with</label>
<%= f.email_field :email, required: true, autofocus: true, placeholder: "Email" %>
<%= f.submit "Submit", class: "pass-reset-submit btn btn-success btn-sm" %>
<% end %>
</div>
So there's a javascript link where an input field will show up if a user forgets their sign in credentials.
apparently two forms for same object having same fields should not be one page, one should stay with the new page. but still i have tried your question, and following things needs to be done
1) I have to override the passwords controller for devise under user scope.
class Users::PasswordsController < Devise::PasswordsController
# GET /resource/password/new
def new
super
end
# POST /resource/password
def create
self.resource = resource_class.send_reset_password_instructions(resource_params)
if successfully_sent?(resource)
flash[:notice] = "sent password"
redirect_to :root
else
render "devise/sessions/new"
end
end
# GET /resource/password/edit?reset_password_token=abcdef
def edit
super
end
# PUT /resource/password
def update
super
end
protected
def after_resetting_password_path_for(resource)
super(resource)
end
# The path used after sending reset password instructions
def after_sending_reset_password_instructions_path_for(resource_name)
super(resource_name)
end
end
then my devise/sessions/new page will look like this(you can add the logic of showing form only when one clicks he forget password button. that should be simple. just add hide class and on click remove hide class.)
Log in
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<% if devise_mapping.rememberable? -%>
<div class="field">
<%= f.check_box :remember_me %>
<%= f.label :remember_me %>
</div>
<% end -%>
<div class="actions">
<%= f.submit "Log in" %>
</div>
<% end %>
#assuming that devise_mapping has recoverable? option. you can also keep the below form in if condition
<h2>Forgot your password?</h2>
<%= form_for(resource, as: resource_name, url: password_path(resource_name), namespace: "forget", html: { method: :post }) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div class="actions">
<%= f.submit "Send me reset password instructions" %>
</div>
<% end %>
need to tell the routes to use my my passwords controller.
devise_for :users, controllers: {
passwords: 'users/passwords'
}
These things will result in showing the errors under the user sign in form but the path will remain http://localhost:3000/users/password. why because we are rendering the page and not redirecting. render just show the views without going to the controller action. now even if one tries to send the errors messages to the sessions controller somehow(after overriding that controller as well) somehow like this
redirect_to new_user_session_path, :messages => resource.errors
still that wont help, why because in session#new we are re initializing the resource as it is new action and all the errors would be gone.
I'm not sure if this is satisfactory to you or if this is not even close to your requirements. i tried to cover all things. i would be glad if some credible or official sources will provide even better response. that would definitely increase my knowledge as well.

No route matches [POST] "/users/sign_up.user"

I incorporated devise in my user model but when i try to sign up someone i get this error No route matches [POST] "/users/sign_up.user"
I have the devise views and controllers in my app too. But when i click the sign up button i get that error.
this is my routes.rb
Rails.application.routes.draw do
devise_for :users, controllers: { registrations: "users/registrations" }
#resources :users
#resources :admin
devise_scope :user do
root :to => 'devise/sessions#new'
end
end
and this is my form
Sign up
<%= form_for(resource, as: resource_name, url: new_user_registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div class="field">
<%= f.label :email %><br />
<%= f.email_field :email, autofocus: true %>
</div>
<div class="field">
<%= f.label :password %>
<% if #validatable %>
<em>(<%= #minimum_password_length %> characters minimum)</em>
<% end %><br />
<%= f.password_field :password, autocomplete: "off" %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation, autocomplete: "off" %>
</div>
<div class="actions">
<%= f.submit "Sign up" %>
</div>
<% end %>
<%= render "users/shared/links" %>
I had this exact same problem, and struggled with what was up for a while. My solution might not have anything to do with your particular case, but worth a try.
In my view I had used erb's button_to tag to hit the new_user_registration_path:
<%= button_to 'Sign up', new_user_registration_path %>
this was sending a POST request when the route is a GET request. Changing the button_to tag to a link_to tag solved it:
<%= link_to 'Sign up', new_user_registration_path %>
Since you changed the route to:
devise_for :users, controllers: { registrations: "users/registrations" }
There's no need for the resource in the path you're using in your form. Simply take it out and it should look like this:
<%= form_for(resource, as: resource_name, url: new_user_registration_path) do |f| %>

Resources