2

I have a website that used a normal Devise login with email and password.

Then I have completed this tutorial to add Omniauth / SSO:

https://github.com/heartcombo/devise/wiki/OmniAuth:-Overview

Now when some users choose to use "Sign in with Google" I want to disable the normal login with email and password for those users (for enhanced security).

How can I achieve that?

collimarco
  • 34,231
  • 36
  • 108
  • 142
  • There is no in-built method to achieve this. you can simply manage flag in user model and override devise sign-in method to write your own logic. – Hardik Upadhyay Jun 27 '22 at 11:52
  • @HardikUpadhyay The problem is that I don't want to use monkey patching or other unsafe workarounds... I wonder if there is at least a specific method that I can use to tell Devise to disable the login with email for those users. The only method that I found in the documentation can block the user *completely* and not only the login with email (so it's not useful in my case). – collimarco Jun 28 '22 at 08:59
  • I understand your concern but as far I know there is no inbuilt method available to achieve this, you have to override sessions controller. if you find anything please let me know as well. – Hardik Upadhyay Jun 28 '22 at 09:25
  • FYI: https://stackoverflow.com/a/6004353/6270554 – Hardik Upadhyay Jun 28 '22 at 09:32
  • @HardikUpadhyay It's exactly the method that I already found: the problem with `active_for_authentication?` is that it would block the user sign-in in general, including the sign in with Google, not just the sign-in with email... – collimarco Jun 28 '22 at 12:30

2 Answers2

2

I'm facing in a similar situation and I'm not sure there's a clean way to do it.

I'm doing what krsyoung is suggesting on this thread https://github.com/heartcombo/devise/issues/5502#issuecomment-1445633830

# user.rb

  def valid_password?(password)
    return false if sso_enabled?

    super
  end

Extra: since I have to disallow unlock-ing accounts and resetting passwords but also have to obfuscate the validation messages by showing the default devise messages, I had to overwrite some more devise methods

# also in user.rb

  def send_reset_password_instructions?
    email_login_enabled? # your logic can go here
  end

  def send_reset_password_instructions
    # still show the `send_paranoid_instructions` message
    # "If your email address exists in our database, 
    # you will receive a password recovery link at your email 
    # address in a few minutes."
    return unless send_reset_password_instructions?

    super
  end

  def send_unlock_instructions?
    email_login_enabled?  # your logic can go here
  end

  def send_unlock_instructions
    # Still shows the `send_paranoid_instructions`
    # "If your account exists, you will receive an email 
    # with instructions for how to unlock it in a few minutes."
    return unless send_unlock_instructions?

    super
  end
0

active_for_authentication? (which you already know) method is used to control if devise should allow a user to login. If you return false for omniauth user then it will also disable login for omniauth user when user is trying to login using omniauth flow. So it's not an option.

Option 1:

active_for_authentication? does not know if user tries to login using password or omniauth. If we manage to know which flow is followed then we can return false when omniauth user tries to login using password.

One way to know which path is followed by setting session when user tries to login using omniauth flow and check for it in active_for_authentication?

In omniauth flow:

session[:omniauth_login] = true

and in active_for_authentication?

def active_for_authentication?
  super && (!omniauth_user? || session[:omniauth_login])
end

It will allow regular users to sign in, block omniauth users sign in by password and allow omniauth users to sign in using omniauth flow (where session is set.). Also reset session after login.

Option 2:

You can override sessions_controller and block access for omniauth enabled users.

class SessionsController < Devise::SessionsController
  def create
    #Allow login with password for all except omniauth users    
    #Define your own logic inside omniauth_user?

    if omniauth_user?(sign_in_params)                  
      redirect_to new_user_session_path, alert: "You can't login using password"
      return
    end

    super
  end
end
Sharjeel
  • 15,588
  • 14
  • 58
  • 89