Can't unlink omniauth linkedin account from devise user, due to need of user password
New here? Learn about Bountify and follow @bountify to get notified of new bounties! x

I created an app, where users can create or associate their Linkedin accounts to a devise user, using Omniauth. The signup, login and account association, are working fine, but i'm not able to unlink the account, since devise requires user to enter his password, to unlink the account:

Here's my user model:

class User < ActiveRecord::Base

  attr_accessible :email, :full_name, :password, :password_confirmation, :remember_me, :provider, :uid
  attr_accessible :role, :as => :admin

  # Include default devise modules. Others available are:
  # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable
  devise :database_authenticatable, :omniauthable, :registerable, :recoverable, :rememberable, :trackable, :validatable


  # Validations
  validates :full_name, :presence => true, :length => { :in => (2..100) }

  #OmniAuth
  def self.from_omniauth(auth)
    where(auth.slice(:provider, :uid)).first_or_create do |user|
      user.provider = auth.provider
      user.uid = auth.uid
      user.full_name = auth.info.name
      user.email = auth.info.email
      # user.password = Devise.friendly_token[0,20]
    end
  end

  def self.new_with_session(params, session)
    if session["devise.user_attributes"]
      new(session["devise.user_attributes"], without_protection: true) do |user|
        user.attributes = params
        user.valid?
      end
    else
      super
    end
  end

  def password_required?
    super && provider.blank?
  end

   def update_with_password(params, *options)
     if encrypted_password.blank?
       update_attributes(params, *options)
     else
       super
     end
   end
end

Here's my callbacks controller:

class OmniauthCallbacksController < Devise::OmniauthCallbacksController
  def all
    user = User.from_omniauth(request.env["omniauth.auth"])
    if user.persisted?
      flash.notice = "Sucessfully logged in with Linkedin!"
      sign_in_and_redirect user
    else
      session["devise.user_attributes"] = user.attributes
      redirect_to new_user_registration_url
    end
  end
  alias_method :linkedin, :all
end

Here's the route:

Dealbook::Application.routes.draw do

  devise_for :users, path_names: {sign_in: "login", sign_out: "logout"},
                controllers: {omniauth_callbacks: "omniauth_callbacks"}
end

Here's the devise/registrations/edit view:

<div class="title form-title">
  <h3>Edit <%= resource_name.to_s.humanize %></h3>
</div>

<%= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put, :class => 'form-horizontal' }) do |f| %>
  <%= f.error_notification %>

  <div class="inputs">
    <%= f.input :full_name, :required => true, :autofocus => true %>
    <%= f.input :email, :required => true %>
    <%= f.input :password, :hint => "leave it blank if you don't want to change it", :required => false %>

        <%= f.input :password_confirmation, :required => false %>
        <% if f.object.encrypted_password.present? %>
                <%= f.input :current_password, :hint => "we need your current password to confirm your changes" %>
    <% end %>

    <% if f.object.provider == 'linkedin' %>
            <%= image_tag('linkedin.png')%> <a href="#" id="unlink_linkedin">Unlink Linkedin account</a>
    <% else %>
            <%= image_tag('linkedin.png')%> <%= link_to "Associate a Linkedin account", user_omniauth_authorize_path(:linkedin), :id => 'signup_link' %>
    <% end %>
  </div>

  <div class="form-actions">
    <%= f.button :submit, "Update", :class => 'btn-primary' %>
  </div>
<% end %>

<div class="bottom-links">
<!--   <h4>Cancel my account</h4> -->

  <p>Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), :confirm => "Are you sure?", :method => :delete %>.</p>

  <%= link_to "Back", :back %>
</div>


<script>
    $(document).ready(function() {
        $("#unlink_linkedin").click(function (){
           if (confirm("Are you sure ?")){
             $('#user_provider').val("nil");
             $('#user_uid').val("");
             $('#edit_user').submit();
           }
         });
     });
</script>
Please format your code. It's a mess.
alixaxel over 6 years ago
I can't format it using ``bizarre. I really need help from someone!
xcode over 6 years ago
I added an answer below, but it occurred to me that this might be the issue (still not sure what you mean when you say devise requires a password): https://github.com/plataformatec/devise/wiki/How-To%3a-Allow-users-to-edit-their-account-without-providing-a-password
EdS over 6 years ago
If a user signed up via LinkedIn and then unlink it, how will he sign in later? This is not concerning unlinking without a password, but you should handle this also.
nimf over 6 years ago
awarded to EdS

Crowdsource coding tasks.

2 Solutions

Winning solution

I'm not really sure what you mean when you say that devise requires the user to enter their password, but I think I see the issue:

It looks like the hidden fields for #user_provider and #user_uid are missing from the form.

It's nice to make a separate controller for these sorts of actions. That way you don't need the JS at the end of the form, and the user can't enter in a bogus UID or provider (not that they'd want to, but it's less than ideal to let them have the option). Here is the new controller you can add:

class UnlinkController << ApplicationController
  def create
    @user = User.find(params[:id])
    @user.unlink_from_linkedin
    redirect_to @user, notice: "Your LinkedIn account has been unlinked from your profile."
  end
end

In your User.rb:

def unlink_from_linkedin
  update_column(:uid, nil)
  update_column(:provider, nil)
end

In your routes:

post "/users/:id/unlink" => "unlink#create", as: "unlink"

In your /edit.html.erb

<%= link_to "Unlink from your LinkedIn", unlink_path(@user), method: :post %>

cool! Now i could unlink the linkedin account! Would you be able to do a quick skype call with me to help with some other issues in this implementation? I'd be happy to pay for your time.

xcode over 6 years ago
Hi xcode, thanks for accepting my solution! I've actually been pretty busy recently (sorry took so long to respond to your comment), and probably won't have time to help you further on your project. But I'll keep an eye out for other questions you post on this site!
EdS over 6 years ago

Feel free to swipe the OAuth code from my project. It has the benefit of also allowing you to add multiple authentications per user (e.g., twitter, facebook, AND github), as well as solving your problem with not being able to remove them:

https://github.com/johnhutch/Tinder

View Timeline