13

I'm using Doorkeeper for my Rails app, and I'm trying to make so that when a user signs out from the doorkeeper provider, the user will automatically signs out from all apps.

By default, when a user signs out from an app, he will still be signed in at the doorkeeper provider app.

This is my session controller from my Doorkeeper provider.

class SessionsController < ApplicationController
  def new
    redirect_to root_path if current_user
    session[:return_to] = params[:return_to] if params[:return_to]
  end

  def create
    user = User.find_by_email(params[:email])
    if user && user.authenticate(params[:password])
      session[:user_id] = user.id
      if session[:return_to]
        redirect_to session[:return_to]
        session[:return_to] = nil
      else
        redirect_to root_path

      end
    else
      flash.now.alert = "Email or password is invalid"
      render "new"
    end
  end

  def destroy
    session[:user_id] = nil
    flash[:alert] = "Sign Out successfully"
    redirect_to new_session_path
  end
end

This is my session controller from one of my app:

    class SessionsController < ApplicationController
  def create
    auth = request.env["omniauth.auth"]
    user = User.find_by_provider_and_uid(auth["provider"], auth["uid"]) || User.create_with_omniauth(auth)
    session[:user_id] = user.id
    session[:access_token] = auth["credentials"]["token"]
    redirect_to root_url
  end

  def destroy
    session[:user_id] = nil
    session[:access_token] = nil
    redirect_to root_url
  end
end

I wrote my own user authentication for the Doorkeeper provider app, but I used Devise for own of my app connected to my Doorkepeer provider app.

At the moment, when I sign out from my Doorkeeper app, I'm still signed in at my other app. So how do I make so that I sign out from Doorkeeper, and that will make me sign out from all apps as well?

Amirol Ahmad
  • 532
  • 5
  • 21
  • Do the apps share the DB? – bbozo Aug 04 '15 at 10:21
  • @bbozo no, apps don't share the databases. i have a separate user table in my client app. – Amirol Ahmad Aug 05 '15 at 06:44
  • Hm, maybe I'm wrong, but as far as I understand doorkeeper just authenticates the user initially, the individual app session stores handle things from there and don't ping oauth provider on every request, perhaps if you used a common session store for all apps? It should be enough to override `session` to return `user_id` and `access_token` from a memcache or redis server that all apps share? – bbozo Aug 05 '15 at 07:16
  • If you can confirm that's indeed how it works I'll write you up some example code how to do it :) – bbozo Aug 05 '15 at 07:49
  • @bbozo yes, that’s how it works. Would be great if you can provide example code how to do it – Amirol Ahmad Aug 10 '15 at 02:51

2 Answers2

4

you would have to either send an API call from the doorkeeper app to each client app telling them to remove sessions for specific user, or you would need to have your client app query the doorkeeper app regularly to ensure that the session or access token is still active. The latter is probably the better strategy, although it will end up making more API calls.

Eugene G
  • 456
  • 4
  • 11
  • 2
    Quick update: I've since implemented such a solution. The doorkeeper app will accept requests to /session/destroy along with an access token. It will then destroy any existing session for that token. Each app sends such a message upon sign out. – Eugene G Oct 09 '15 at 02:19
2

The way I see it, you can sync states between the apps either by a relatively complex exchange of API calls (prone to network errors, "one of my apps was just restarting", ... etc) or through introduction of a shared storage, the best options are probably a shared memcached or a shared redis server that will store access tokens for your user ids

One way to tackle this is to override session in ApplicationController of all of your doorkeeper client apps and to have them ping the shared storage when something wants to get or set :access_token.

Note that I'm not a fan of hash [] and []= overriding because it violates the principle of least surprise, but I feel this is the best approach in this case.

I give you an example code for overriding the instance of session to do just this: https://gist.github.com/bbozo/f5c28fe9bd804dff0af8 using the Dalli memcached client, things to note:

Note that I'm not 100% sure that overriding methods of an instance does not come with an obscure method lookup regression, so if you have control over the instantiation of session it's probably safest to just create a class that inherits from HashWithIndifferentAccess with the 2 overrides from the gist and then instantiate this instead of the default hash class, but really don't bother yourself about this unless you expect some truly serious traffic.

bbozo
  • 7,075
  • 3
  • 30
  • 56