Search code examples
clearance

Clearance failure when forbidden password reset


I’m using clearance and love it, but I'm having trouble resetting passwords. I type in my email to reset the password, which works, but then when I try to navigate to the edit password page using the reset token, I get the failure when forbidden flash error “Please double check the URL or try submitting the form again” and it redirects me back. I get the same error in my tests.

I think this has something to do with my before_action statements, but I just don’t know how to fix them. I have researched questions like this to no avail.

I'm sure it's a stupid question, but I'm new so I really appreciate any help. Please let me know if this isn't enough code.

class UsersController < Clearance::UsersController
  before_action :require_login, only: [:create] # does this need to be in both user controllers?

  ...

  def user_params
    params.require(:user)
  end
end

And here is the clearance controller.

class Clearance::UsersController < ApplicationController
 before_action :require_login, only: [:create]
 require 'will_paginate/array'

  def new
     @user = user_from_params
     render template: 'users/new'
   end

  def create
     @user = user_from_params
     @user.regenerate_password

     if @user.save
       sign_in @user unless current_user
       UserMailer.welcome_email(@user).deliver!
       redirect_to users_path
     else
       render template: 'users/new'
     end
   end

  def edit
     @user = User.friendly.find(params[:id])
   end

   def update
     @user = User.friendly.find(params[:id])
     if @user.update(permit_params)
       redirect_to @user
       flash[:success] = "This profile has been updated."
     else
       render 'edit'
     end
   end

private

def avoid_sign_in
  redirect_to Clearance.configuration.redirect_url
end

def url_after_create(user)
  dashboards_path(user)
end

def user_from_params
 user_params = params[:user] || Hash.new
 is_public = check_public_params(user_params)

 first_name = user_params.delete(:first_name)
 last_name = user_params.delete(:last_name)
 email = user_params.delete(:email)
 password = user_params.delete(:password)
 parish = user_params.delete(:parish)
 division = user_params.delete(:division)
 admin = user_params.delete(:admin)

 Clearance.configuration.user_model.new(user_params).tap do |user|
   user.first_name = first_name
   user.last_name = last_name
   user.password = password
   user.email = email
   user.is_public = is_public
   user.parish_id = parish.to_i
   user.division = division
   user.admin = admin
 end
end

def permit_params
    params.require(:user).permit(:first_name, :last_name, :email, :password, :is_public, :parish_id, :division, :admin)
  end
end

EDIT: relevant portions of routes.rb

Rails.application.routes.draw do
  resources :passwords, controller: "clearance/passwords", only: [:create, :new]
  resource :session, controller: "clearance/sessions", only: [:create]

  resources :users, controller: "clearance/users", only: [:create] do
    resource :password,
      controller: "clearance/passwords",
      only: [:create, :edit, :update]
  end

  get "/sign_in" => "clearance/sessions#new", as: "sign_in"
  delete "/sign_out" => "clearance/sessions#destroy", as: "sign_out"
  get "/sign_up" => "clearance/users#new", as: "sign_up"
    constraints Clearance::Constraints::SignedOut.new do
    root to: 'high_voltage/pages#show', id: 'landing'
  end

  constraints Clearance::Constraints::SignedIn.new do
    # root to: 'dashboards#index', as: :signed_in_root
    root to: 'high_voltage/pages#show', id: 'parish_dashboard', as: :signed_in_root
  end

  # constraints Clearance::Constraints::SignedIn.new { |user| user.admin? } do
  #   root to: 'teams#index', as: :admin_root
  # end

  resources :users do
    collection { post :import }
  end

Solution

  • It turns out there was a conflict between the way I was finding the user instance in the password reset link. Clearance finds users simply by using @user, but since I'm using FriendlyId I needed to change that to @user.id.

    So instead of...

    <%= link_to 'Change My Password', edit_user_password_url(@user, token: @user.confirmation_token.html_safe) %>
    

    I did

    <%= link_to 'Change My Password', edit_user_password_url(@user.id, token: @user.confirmation_token.html_safe) %>
    

    Thanks, Thoughbot, for this great gem!