ruby-on-railsrubyoauth-2.0devisedoorkeeper

Authorization Server with Rails 7, Devise and Doorkeeper


I'm working on building a Rails 7 server with Devise and Doorkeeper.

We have two groups of users this will support, one that omniauths to am ouath provider and one that authenticates to the database.

enter image description here

I'm able to oauth to the provider, and I can add doorkeeper and get the PKCE flow to basically work BUT I seem to loose the application that starts the PKCE flow when I'm in the omniauth callback.

The code to initiate the authentication flow is using (via doorkeeper)

resource_owner_authenticator do
  current_user || warden.authenticate!(scope: :user)
end

and this works, and I get to my successful omniauth callback

def omniauth_successful_callback
  User.find_with_omniauth(<<request information>>)
  
  redirect_to <<redirect_page_path>>
end

This is fine, if I'm logging into the rails application, but when I'm using the PKCE flow I need to redirect back to the initiating applications, but I don't seem to have access to when I come back from omniauth.

How can I redirect to the right application using the PKCE flow and omniauth programmatically?

I've tried looking for doorkeeper token, reading documentation, and to test I could get back to complete the flow I've hardcoded the redirect for the application I was testing. I can't seem to find the application on the return. I'm currently exploring trying to send the application name to the oauth provider to have it echoed back to me, so I can get the redirect, but that seems hacky.

I'm hoping that I can get someone to show me where I can find the application during the omniauth successful callback so I can redirect to the right location


Solution

  • There is no support for this, you have to build it, but it's not really that difficult.

    When your client reaches your server in an OAuth/OIDC flow, and you want to delegate further to some downstream OAuth provider to actually perform the authorization, it's up to you to store the original authorization request parameters, and to restore them when the user comes back from the downstream OP. You can easily do this just by storing the entire URL, including the query string.

    You would typically do this in the session, possibly associating the request with some kind of ID so you can be sure the user is coming back as part of the same authorization flow (as opposed to the user abandoning the flow, and then coming back later through some other path). You would send that ID to the OP in the state parameter, which is sent back to you unmodified.

    That might be something as simple as this:

    rid = SecureRandom.uuid # request ID
    session[:auth_req] = {
       # Something like /auth?client_id=xxx&response_type=code&redirect_uri=yyy&etc
      rid: request.full_path
    }
    
    # redirect to Omni-auth endpoint, passsing `rid` in `state` parameter
    

    In your callback:

    rid = params[:state]
    auth_req_path = session[:auth_req][rid]
    if auth_req_path
      # The user is now authenticated and we can "resume"
      # the original authorization request
      redirect_to auth_req_path
    else
      # default action or error message
    end