googleads / google-api-ads-ruby

Ad Manager SOAP API Client Libraries for Ruby
297 stars 229 forks source link

Passing stateful information in initial authorization request? #193

Closed mustmodify closed 1 year ago

mustmodify commented 1 year ago

I'm sorry to say I'm having a hard time following your documentation. Here's a very trivial proof-of-concept I have working. I would like to add a token to this that you will pass back to me which will include a necessary id. I was able to do this recently with the HubSpot oAuth API using a "state" key. So for example, it might be something like:

class GoogleAdConnection
  API_VERSION = :v202208

  def initialize
    data = {
      method: 'OAuth2',
      oauth2_client_id: ENV['GAM_CLIENT_ID'],
      oauth2_client_secret: ENV['GAM_SECRET_ID'],
      application_name: 'Google Ad Manager Client'
    }

    @ad_manager = AdManagerApi::Api.new({
      :authentication => data,
      :service => {
        :environment => 'PRODUCTION'
      }
    })
  end

  def redirect_url
    begin
      @ad_manager.credential_handler.set_credential('returnable_state', '42552')
      @ad_manager.authorize(oauth2_callback: 'https://domain.com/partners/google/auths/grant')
    rescue AdsCommon::Errors::OAuth2VerificationRequired => e
      puts e.oauth_url
    end
  end
end

Again this is just a proof-of-concept. But is there a way to add a parameter that will be passed back?

mustmodify commented 1 year ago

Updated first step -- use the oauth2_state parameter when initializing AdManagerApi::Api. First step code below:

class GamAccessRequest
  CLIENT_ID = ENV['GAM_CLIENT_ID']
  SECRET_ID = ENV['GAM_SECRET_ID']
  APP_URL = some way to figure out 'https://www.whatever.com'

  def initialize(some_id: nil)
    @some_id = some_id
  end

  def valid?
    CLIENT_ID.present? && @some_id.present?
  end

  def params
    {
      method: 'OAuth2',
      oauth2_client_id: CLIENT_ID,
      oauth2_client_secret: SECRET_ID,
      oauth2_state: encoded_state,
      application_name: 'Google Ad Manager Client',

      # https://stackoverflow.com/a/10857806
      oauth2_prompt: 'consent',
      oauth2_access_type: 'offline'
    }
  end

  def ad_manager
    AdManagerApi::Api.new({
      :authentication => params,
      :service => {
        :environment => 'PRODUCTION'
      }
    })
  end

  def callback_url
    APP_URL + '/partners/google/auths/grant'
  end

  def wonky_url_acquisition
    begin
      ad_manager.authorize(oauth2_callback: callback_url)
    rescue AdsCommon::Errors::OAuth2VerificationRequired => e
      e.oauth_url.to_s
    end
  end

  def url
    @url ||= wonky_url_acquisition

    if @url.nil?
      Sentry.capture_message("Expecting a URL here but got none. params: #{params.to_json}")
    end

    @url
  end

  def state_data
    {network_id: @network_id}
  end

  def encoded_state
    encryptor = ActiveSupport::MessageEncryptor.new(SMALL_SECRET_KEY)
    secret = state_data.to_json
    encryptor.encrypt_and_sign(secret)
  end
end