1

in views, there are idiom like this for #create and #update actions

<% if @article.errors.any? %>
  <div id="error_explanation">
    <h2>
      <%= pluralize(article.errors.count, "error") %> prohibited
        this article from being saved:
    </h2>
    <ul>
      <% @article.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
      <% end %>
    </ul>
  </div>
<% end %>

Now, is there similarly a way to check in the Users::SessionsController < Devise::SessionsController, whether the user-login was successful or not?

There is a similar thread here Ruby on Rails Devise code after login but the answer by Peter P. Jan 18 '15 at 3:06 suggests, that exactly when the login is unsuccessful, then the before_action "will not be run", which is bad, because I need exactly this information, whether the login was unsuccessful.

The accompanying code is

# app/controllers/custom_sessions_controller.rb
class CustomSessionsController < Devise::SessionsController
  ## for rails 5+, use before_action, after_action
  before_filter :before_login, :only => :create
  after_filter :after_login, :only => :create

  def before_login
  end

  def after_login
  end
end

But obviously useless towards my problem... (?)

The suggestions are to use Warden hooks like this by User Mike Lapinskas, Oct 8 '16 at 5:33, but the content too doesn't seem to be an answer to my problem. It is about after login, not a test whether the login was unsuccessful

class User < ApplicationRecord
  Warden::Manager.after_set_user do |user, auth, opts|
    if (opts[:scope] == :user && opts[:event] == :set_user)
      # < Do your after login work here >
    end
  end
end

The approach in this thread Devise: redirect on sign up failure? seems promising

The hint to Devise::FailureApp led me to https://gist.github.com/emilsoman/5604254, also see Custom Devise 401 unauthorized response

The responses mentioned there are all JSON. Is this mandatory? Can't I just call render on an html.erb-template/partial ?

Thanks

Jai Chauhan
  • 4,035
  • 3
  • 36
  • 62
von spotz
  • 875
  • 7
  • 17

1 Answers1

2

If you want to execute an post-sign in method, just create a custom controller, inherit Devise::SessionsController and pass a block to super. Devise will call that block after signing in successfully.

https://github.com/heartcombo/devise/blob/57d1a1d3816901e9f2cc26e36c3ef70547a91034/app/controllers/devise/sessions_controller.rb#L22

class CustomSessionsController < Devise::SessionsController
  def create
    super do |resource|
      # do something after signing in successfully
    end 
  end
end

To handle failure case, you can catch the exception which raised by warden's #authenticated! (←I'm wrong about this) https://github.com/heartcombo/devise/blob/57d1a1d3816901e9f2cc26e36c3ef70547a91034/app/controllers/devise/sessions_controller.rb#L19

If your logic is complicated, and you need to custom a lot of code, just write your own #create without super, and use devise's provided method to achive your requirement. Keep in mind that devise has some callbacks which might affect your custom code.

class CustomSessionsController < Devise::SessionsController
  def create
    self.resource = warden.authenticate(auth_options) # Without `!`
    if resource
      # success
    else
      # failure
    end
  end
end
moonlight8978
  • 156
  • 1
  • 3
  • Hello moonlight, thanks for your answer. You obviously are by far more knowledgable about warden (and devise) than me. I don't even know which of the three `warden.authenticate!` definitions is the actual one, which is invoked here and why it is invoked. Isn't the approach over Devise::FailureApp easier (for beginner-level at least?) or even more apt ? Thank you for your competent help. Best wishes, von Spotz. – von spotz May 06 '21 at 08:22
  • However, even with `Devise::FailureApp`, how would I "detect" that the login failed in my subclass `class CustomAuthenticationFailure < Devise::FailureApp` ? Thanks – von spotz May 06 '21 at 08:38
  • For example, if I intentionally enter a wrong password, I get the error message "Invalid Email or password." But looking at the source login procedures of devise or warden, I cannot make out a subroutine or hook for a failed login. – von spotz May 06 '21 at 09:19
  • you're right, using `Devise::FailureApp` is better and simple in your case. Since `Devise::FailureApp` got called only in case that the request is invalid, you only need to check whether the request is login request or not. (The docs said: Failure application that will be called every time :warden is thrown from any strategy or hook -> this might be called on other request as well) ```ruby class CustomAuthenticationFailure < Devise::FailureApp def respond # ... some process super end end ``` `FailureApp` has `#request` variable, you can use that var to check – moonlight8978 May 06 '21 at 09:34
  • sorry for late reply, I'm not familiar with `Devise::FailureApp`, so it took me some time to debug. about `Devise::FailureApp#request`, you can check available methods here: https://github.com/rails/rails/blob/main/actionpack/lib/action_dispatch/http/request.rb for insensitive callbacks, `request.params[:action]` and `request.controller_class` should be enough. but for sensitive one, `params[:action]` should not be relied on. – moonlight8978 May 06 '21 at 09:39
  • Hello moonlight. I'm sorry, what you say is probably well understandable for rails programmers with more experience like with experienced mathematicians who begin to skip actually important parts of notation when talking to other experienced mathematicians. It's not that I am lazy and I wouldn't have tried to puzzle together your hints. I am thankful to you for letting me know them. But to me it's still too much omitted. I don't come to a conclusion. Blame it on me being a b00n. Would you maybe be so kind to post a complete example? Best wishes. Thanks. von Spotz – von spotz May 06 '21 at 11:31