Skip to content

Instantly share code, notes, and snippets.

@traumverloren
Last active March 10, 2022 10:27
Show Gist options
  • Save traumverloren/eb7c27e1e2780240c13f to your computer and use it in GitHub Desktop.
Save traumverloren/eb7c27e1e2780240c13f to your computer and use it in GitHub Desktop.
doorkeeper config for redirect back to client app after login with oauth2 provider
#########################
# config/initializers/doorkeeper.rb
#########################
Doorkeeper.configure do
# Change the ORM that doorkeeper will use.
# Currently supported options are :active_record, :mongoid2, :mongoid3, :mongo_mapper
orm :active_record
# This block will be called to check whether the resource owner is authenticated or not.
resource_owner_authenticator do
User.find_by_id(session[:user_id]) || redirect_to(new_session_url(return_to: request.fullpath))
end
#########################
# sessions_controller.rb
#########################
class SessionsController < ApplicationController
def new
session[:return_to] = params[:return_to]
end
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
session[:user_id] = user.id
redirect_to session[:return_to] || root_url, notice: "Logged in!"
session.delete(:return_to)
else
flash.now.alert = "Invalid password or email"
render "new"
end
end
end
@jiggneshhgohel
Copy link

With the version 4.2.0 of Devise I was using there following a convention set by Devise for the session variable the need to do any modifications in the SessionsController can be avoided. Following that convention I updated my config to following and it worked without any issues:

Doorkeeper.configure do
  ...

   resource_owner_authenticator do |routes|
    if current_user
      current_user
    else
      # Refer the HERE document at the bottom on why this session variable is being set.
      session[:user_return_to] = request.fullpath
      redirect_to(new_user_session_url)
    end
  end
  ....
end

=begin

 Why this specific session variable? Because Devise looks for this name
 Refer `after_sign_in_path_for(resource_or_scope)` method
 implementation and documentation at

  https://github.com/plataformatec/devise/blob/v4.2.0/lib/devise/controllers/helpers.rb#L213

 and you should get it why.

  https://github.com/plataformatec/devise/blob/v4.2.0/lib/devise/controllers/store_location.rb#L17
  https://github.com/plataformatec/devise/blob/v4.2.0/lib/devise/controllers/store_location.rb#L54

 And `after_sign_in_path_for` method should come into picture when Client-app
 sends an authorization request to this Provider-app and the user is not
 found to be logged-in on the Provider-app. So Devise should throw
 the user to the Sign-in page and after successful sign-in user should
 be redirected to the Client-app. So we are storing the Client-app's
 path here in a session key supported by Devise in out-of-the-box fashion
 for handling redirects to original page after signing-in.

 So just setting this session variable Devise should handle the redirect
 to Client-app AFTER SUCCESSFUL SIGN-IN without any additional changes
 in it's SessionsController implementation.

 References:
  https://dev.mikamai.com/2015/02/11/oauth2-on-rails/
  https://stackoverflow.com/a/21632889/936494

 Note that if this session variable is NOT set then what would happen is
 that when user from Client-app sends authorization request and user
 is not NOT found logged-in by Devise on this Provider-app
 the Provider-app display's Sign Page but after successful login
 Provider-app doesn't redirect the logged-in user to Client-app.

 However that's not the case when user is found logged-in on this Provider-app
 In that case Provider-app displays the Authorize/Deny page facilitated
 by Doorkeeper. Clicking either on Authorize or Deny button the redirect
 happens!


 THIS SHOULD WORK ASSUMING YOU HAVE NOT ALREADY OVERRIDDEN

   after_sign_in_path_for(resource)`

 in your ApplicationController like following


   def after_sign_in_path_for(resource)
     user_home_path
   end


 If that is the case then setting this session variable should have NO EFFECT.
 And to fix that you will need to do this

    def after_sign_in_path_for(resource)
      stored_location_for(resource) || user_home_path
    end

=end

Hope that helps.

Thanks.

@sagar-ranglani
Copy link

@jiggneshhgohel this really helped a lot! Thank you for posting this with such clarity! :-)

Now, Where I'm stuck at is how would I delete the user session when the user clicks logout! We don't have a logout URL with the doorkeeper.

The user should be logged out and when the user logs in again (I'm using identity.launchWebAuthFlow) He should be prompted to enter login details again. Right now, resource_owner_authenticator always has current_user present

 resource_owner_authenticator do |routes|
    if current_user
      current_user
    else
      session[:user_return_to] = request.fullpath
      redirect_to(new_user_session_url)
    end
  end

When I make API calls with the token, the current_user is nil (i.e. warden.authenticated?(:user) is false) so warden.logout does not work when I try to delete the session through API calls.

This SO question discusses that thing to an extent and here the user is logged out right before returning the token. I want to do this (warden.logout) on users sign_out action. Can you help me do this?

In a nutshell, I want to logout the user session which we created during resource_owner_authenticator block when the user clicks logout. (I'm using implicit grant flow for a chrome extension to our API, which is the OAuth provider with Doorkeeper). Any help would be highly appreciated. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment