Coder_Nick
Coder_Nick

Reputation: 851

Customize Devise SessionsController create action

I want to add a feature that will redirect a user with an expired password to the reset password page. My controller looks like this

class Users::SessionsController < Devise::SessionsController

  def create
    user = User.find_by(email: params[:user][:email].downcase)
    if user.password_expire?
      raw, enc = Devise.token_generator.generate(current_user.class,
                                               :reset_password_token)
      user.reset_password_token   = enc
      user.reset_password_sent_at = Time.now.utc
      user.save(validate: false)
      redirect_to edit_password_url(user, reset_password_token: raw)
    else
      self.resource = warden.authenticate!(auth_options)
      set_flash_message(:notice, :signed_in)
      sign_in(resource_name, resource)
      yield resource if block_given?
      respond_with resource, location: after_sign_in_path_for(resource)
    end
  end

end

When I debug with binding.pry at the top of the action, I find that current_user exists and user_signed_in? is true. How is it possible I'm signed in before the create method completes?

Upvotes: 1

Views: 1671

Answers (2)

Shira Elitzur
Shira Elitzur

Reputation: 441

If you are using the security extension you don't need at all to take care of the implementation of password expiration. And if you are not using it, you should check it out - devise_security_extension.

Upvotes: 2

John Gallagher
John Gallagher

Reputation: 6278

Devise remembers the current user using cookies until they log out.

Just because they've hit your sign in route doesn't mean they're signed out.

Diving into Devise

Tracing the code in Devise to understand what happens we see:

1. Devise::SessionsController#create

class Devise::SessionsController < ApplicationController
  # ...
  def create
    # ...
    sign_in(resource_name, resource)
    # ...
  end
end

2. Devise::Controllers::Helpers#sign_in

  def sign_in(resource_or_scope, *args)
    # ...
    if options[:bypass]
      warden.session_serializer.store(resource, scope)
    elsif warden.user(scope) == resource && !options.delete(:force)
      # Do nothing. User already signed in and we are not forcing it.
      true # <=== Here's the moment of truth
    else
    # ...
  end

Conclusion

  • The user can hit your sessions#create when they're already logged in
  • In this case the default Devise behaviour is to do nothing
  • Not quite sure what you want to achieve above, but calling super might come in handy somewhere to resume the default Devise behaviour

Upvotes: 1

Related Questions