socialcast / devise_oauth2_providable

Rails3 engine integrating OAuth2 authentication with Devise
MIT License
219 stars 102 forks source link

Asking to approve the app each time #31

Closed gregwinn closed 12 years ago

gregwinn commented 12 years ago

Hello, I have an issue, and this could be my own issue but looking for an answer. Each time the user logs in it's asking to "Approve" or "Deny", should this only take place once or until the refresh token expires or the user removes the app from the approved list?

Am i missing something?

Request in order:

/oauth2/authorize "Approve" or "Deny" (every time) /oauth2/ token (Grant type Authorization code)

Is there a way for the user to ONLY "Approve" or "Deny" once?

Thanks in advance!

gregwinn commented 12 years ago

Anyone have an solution to this?? Or is anyone seeing the same thing?

denyago commented 12 years ago

Not sure, if it's good idea. But as a temporary solution, you may try this:

config/roures.rb :

scope '/oauth2', :as => 'oauth2' do
    match 'authorize' => 'oauth_authorizations#new'
  end

app/controllers/oauth_authorizations_controller.rb :

class OauthAuthorizationsController < Oauth2::AuthorizationsController
  def new
    params[:approve] = true
    respond *authorize_endpoint(:allow_approval).call(request.env)
  end
end
gregwinn commented 12 years ago

Thank you, i will give this a try and let you know!

gregwinn commented 12 years ago

This solution does work but it approves the user even if they have not approved the client. For me this is only a temp solution... I am looking for a long term fix so the user can approve or deny the client only once.

Here is my controller:

class OauthAuthorizationsController < Devise::Oauth2Providable::AuthorizationsController
    def new
        params[:approve] = true
        respond *authorize_endpoint(:allow_approval).call(request.env)
    end
end
denyago commented 12 years ago

Add before filter to :create action and save approved clients to DB. On the :new action - get approved clients of user and authorize (or not).

joe1chen commented 12 years ago

Not sure if this is the right solution, but in my branch of devise_oauth2_providable, I modified the authorize_endpoint function to check for a valid refresh token. If I have a valid refresh token stored already, then I force params[:approve] to true and allow_approval to :allow_approval.

authorizations_controller.rb

def authorize_endpoint(allow_approval = false)
        Rack::OAuth2::Server::Authorize.new do |req, res|
          @client = Devise::Oauth2Providable::Client.find_by_client_identifier(req.client_id) || req.bad_request!
          res.redirect_uri = @redirect_uri = req.verify_redirect_uri!(@client.redirect_uri)

          # Auto-approve if we have a valid refresh token.
          valid_refresh_token = get_valid_refresh_token
          if valid_refresh_token
            params[:approve] = true
            allow_approval = :allow_approval
          end

          if allow_approval
            if params[:approve].present?
              case req.response_type
              when :code
                authorization_code = current_user.authorization_codes.create!(:client => @client)
                res.code = authorization_code.token
              when :token
                access_token = current_user.access_tokens.create!(:client => @client).token
                bearer_token = Rack::OAuth2::AccessToken::Bearer.new(:access_token => access_token)
                res.access_token = bearer_token
                res.uid = current_user.id
              end
              res.approve!
            else
              req.access_denied!
            end
          else
            @response_type = req.response_type
          end
        end
      end

The get_valid_refresh_token function looks like this:

      def get_valid_refresh_token
        return current_user.refresh_tokens.where(:client_id => @client.id).not_expired.first if current_user
        nil
      end

Note that I'm using Mongoid as my data store, so the query syntax in get_valid_refresh_token is written for Mongoid. But you could modify it for ActiveRecord.

gregwinn commented 12 years ago

Thanks for all the replies! The solution we ended up using was a mix of the first reply and a suggestion from @denyago. We are saving the approval to the DB then checking once the user comes back to see if the client is approved. The user now only sees the approve deny once until they revoke access. Please contact me if you have any questions on how we implemented this solution.

Big thanks to @denyago and @joe1chen