Google Ruby API client - "Missing required parameters: id" - ruby-on-rails

I'm attempting to use the Google Ruby API client to call the Drive API to get a file's metadata, and I'm receiving the error "Missing required parameters: id." Here's the code -- what am I doing wrong? (Note: client id and secret are working fine for requesting access tokens, etc. And the error is "id", not "client id" or "file id").
client = Google::APIClient.new
drive = client.discovered_api('drive')
client.authorization.client_id = ENV["GOOGLE_CLIENT_ID"]
client.authorization.client_secret = ENV["GOOGLE_CLIENT_SECRET"]
client.authorization.redirect_uri = "http://..."
client.authorization.access_token = "..."
client.authorization.scope = "https://www.googleapis.com/auth/drive"
result = client.execute(
:api_method => drive.files.get,
:version => 'v2',
:parameters => { 'fileId' => "..."}
)

Just figured it out. The call to discovered_api was missing the version number. Should be:
drive = client.discovered_api('drive', 'v2')

Related

Ruby on Rails - SoundCloud OAuth 2 undefined method `access_token'

I am working on a making a personal soundcloud app on Rails 5 and i'm having some trouble with OAuth. I'm able to redirect to Soundcloud's app and give permission to my user. When I try to exchange the code for token on redirect_uri, get an error. I have included my code and image of the error I'm getting below.
def connected
client = Soundcloud.new(:client_id => 'My ID',
:client_secret => 'my secret',
:redirect_uri => "http://localhost:3000/login/soundcloud/callback")
code = params[:code]
value = client.exchange_token(:code => code) #get an error on this line
#my code to save access token into db goes here.
end
I have added this image
for the error that I'm getting. I thought it might be more helpful.
I was having the same issues, so I just did created the request manually for this step. Soundcloud gem is pretty dated, I'm sure it has something to do with that.
#client = Soundcloud.new(client_id: client_id,
client_secret: client_secret,
redirect_uri:'http://localhost:3000/soundcloud/connected')
#code = params[:code]
response = HTTParty.post("https://api.soundcloud.com/oauth2/token",
body: {
"client_id" => client_id,
"client_secret" => client_secret,
"redirect_uri" => 'http://localhost:3000/soundcloud/connected',
'grant_type'=> 'authorization_code',
"code" => #code
}
)
access_token = response["access_token"]
#authed_client = Soundcloud.new(access_token: access_token)
soundcloud_user = #authed_client.get('/me')
end
Hope this helps.

How to handle the grant_type error with the google_drive gem?

I'm trying to use the Ruby gem 'google_drive'. Before using that gem, I'm obtaining the user's token via the gem omniauth-google-oauth2.
When I try to use google_drive as follows:
def google_oauth2(current_user)
session = GoogleDrive.login_with_oauth(self.token)
# Gets list of remote files.
session.files.each do |file|
p file.title
end
end
I get the following error:
Sending HTTP get https://www.googleapis.com/drive/v3/files?fields=%2A
Caught error Authorization failed. Server message:
{
"error": "invalid_request",
"error_description": "Required parameter is missing: grant_type"
}
Error - #<Signet::AuthorizationError: Authorization failed. Server message:
{
"error": "invalid_request",
"error_description": "Required parameter is missing: grant_type"
}>
Completed 500 Internal Server Error in 128ms (ActiveRecord: 0.4ms)
How can I resolve this?
Update
Omniauth code being used to store the google oauth 2 tokens:
def google_oauth2
auth_hash = request.env['omniauth.auth']
#authentication = Authentication.find_or_create_by(
user_id: current_user.id,
provider: auth_hash["provider"],
uid: auth_hash["uid"]
)
#authentication.update_attributes(
:token => auth_hash['credentials']['token'],
:refresh_token => auth_hash['credentials']['refresh_token'],
:provider_description => auth_hash["info"].email
)
flash[:notice] = "google_oauth2 authed."
redirect_to '/'
end
Working solution to access google drive api without requiring a config.json. This solution uses the refresh token obtained from the google auth 2 omniauth strategy:
require 'google/apis/drive_v2'
auth = Signet::OAuth2::Client.new(
token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
client_id: "XXX-XXX",
client_secret: "XXX",
refresh_token: self.refresh_token # Get this from the omniauth strategy.
)
auth.fetch_access_token!
x = Google::Apis::DriveV2
drive = x::DriveService.new
drive.authorization = auth
files = drive.list_files
This took me .5 day. I hope it helps someone else out there! :)

Rails: Export data in google spreadsheet

I am new to Rails and I want to export data to Google Spread Sheet from my web application.
I have created an app to get client id and client secret and enabled drive api for that.
I have installed google drive and google api client gem
And I used the code stated here
This code successfully runs, open a new tab for authorization, and displays a code to paste. This is the point where I am stuck. The code that google authorization demands is in my controller code so my user can paste that code in my controller. I know its quiet stupid thing to ask but I am not finding a way to automatically get the code from api to further execution as we usually do in our facebook oauth applications. So can you guide me how to do it? The code looks like
def export_spred_sheet
require 'rubygems'
require 'google/api_client'
require 'launchy'
# Get your credentials from the console
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'
OAUTH_SCOPE = 'https://www.googleapis.com/auth/drive'
REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
# Create a new API client & load the Google Drive API
client = Google::APIClient.new
drive = client.discovered_api('drive', 'v2')
# Request authorization
client.authorization.client_id = CLIENT_ID
client.authorization.client_secret = CLIENT_SECRET
client.authorization.scope = OAUTH_SCOPE
client.authorization.redirect_uri = REDIRECT_URI
uri = client.authorization.authorization_uri
Launchy.open(uri)
# Exchange authorization code for access token
$stdout.write "Enter authorization code: "
client.authorization.code = gets.chomp
client.authorization.fetch_access_token!
# Insert a file
file = drive.files.insert.request_schema.new({
'title' => 'My document',
'description' => 'A test document',
'mimeType' => 'text/plain'
})
media = Google::APIClient::UploadIO.new('document.txt', 'text/plain')
result = client.execute(
:api_method => drive.files.insert,
:body_object => file,
:media => media,
:parameters => {
'uploadType' => 'multipart',
'alt' => 'json'})
# Pretty print the API result
jj result.data.to_hash
end
Or is there any other way to do the task If I am on wrong track?
I was also fighting against this in one of my project and finally I found soulution as follows:
Instead of using client ID and client secrete you can use P12 key generated in google developer console under service account for authentication. In this case you won't need to paste any code in controller.
To generate p12 key
go to Google developer console
then -> "APIs & Auth" -> "Credentials"
Create New Client ID of type 'Service Account'
Once new client Id generated. Under Service Account section you will find a button to 'generate new P12 key'. On click it will generate a p12 key. Download it and store it securely in your app and use it for authentication.
And use following code snippet to fetch access token.
key_file = p12_key_file_path
key = Google::APIClient::KeyUtils.load_from_pkcs12(key_file, 'notasecret')
client = Google::APIClient.new application_name: "abcd", application_version: "1.0.0"
SERVICE_ACCOUNT_ID (used in following code snippet) will be Email address generated under this "Service Account" section in google developer console.
client.authorization = Signet::OAuth2::Client.new(
:token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
:audience => 'https://accounts.google.com/o/oauth2/token',
:scope => 'https://www.googleapis.com/auth/analytics',
:issuer => SERVICE_ACCOUNT_ID,
:access_type => 'offline',
:signing_key => key
)
client.authorization.fetch_access_token!
client
I hope it will help you.

Spotify Web API Bad Request Error "invalid_client" when refreshing token

I'm building an app in Rails using the Spotify web API. I built a method to refresh a user's token, but am receiving a 400 error. According the the Spotify Web API docs, the header of my request needs to be in the following format:
Authorization: Basic <base64 encoded client_id:client_secret>
Using Httparty gem, here's the POST method to refresh the access token:
def refresh_token
client_id = "foo"
client_secret = "bar"
client_id_and_secret = Base64.encode64("#{client_id}:#{client_secret}")
result = HTTParty.post(
"https://accounts.spotify.com/api/token",
:body => {:grant_type => "refresh_token",
:refresh_token => "#{self.oauth_refresh_token}"},
:headers => {"Authorization" => "Basic #{client_id_and_secret}"}
)
end
Here's what "result" ends up being:
=> #<HTTParty::Response:0x7f92190b2978 parsed_response={"error"=>"invalid_client", "error_description"=>"Invalid client secret"}, #response=#<Net::HTTPBadRequest 400 Bad Request readbody=true>, #headers={"server"=>["nginx"], "date"=>["Sun, 31 Aug 2014 22:28:38 GMT"], "content-type"=>["application/json"], "content-length"=>["70"], "connection"=>["close"]}>
I can decode client_id_and_secret and it returns "foo:bar", so I'm at a loss as to why I'm receiving a 400 error. Any insight is much appreciated.
Found the issue... it was with the Base64 encoding in Ruby. Apparently (as shown in Strange \n in base64 encoded string in Ruby) using the Base64.encode64('') method adds an extra line within the code. Using Base64.strict_encode64('') solved the issue.
Updated code:
def refresh_token
client_id = "foo"
client_secret = "bar"
client_id_and_secret = Base64.strict_encode64("#{client_id}:#{client_secret}")
result = HTTParty.post(
"https://accounts.spotify.com/api/token",
:body => {:grant_type => "refresh_token",
:refresh_token => "#{self.oauth_refresh_token}"},
:headers => {"Authorization" => "Basic #{client_id_and_secret}"}
)
end

"Missing authorization code" error when trying to use Google Calendar API

The following is the code I am using to allow users to allow users to authorise my app to access their Google Calendar via OAuth. I based it off this sample code.
It works most of the time, but sometimes, there is an ArgumentError: Missing authorization code error on the client.authorization.fetch_access_token! line in the create_google_calendar action in the services controller. If I comment out that line, all of the client.authorization attributes are null.
I am using Rails 3.2.0 and Ruby 1.9.2.
What is causing this?
Gemfile
gem 'google-api-client', :require => 'google/api_client'
service.rb
def self.google_calendar_client google_calendar_service=nil
client = Google::APIClient.new
client.authorization.client_id = xxx
client.authorization.client_secret = xxx
client.authorization.scope = 'https://www.googleapis.com/auth/calendar'
url_prefix = Rails.env.production? ? xxx : 'http://localhost:3000'
client.authorization.redirect_uri = "#{url_prefix}/create_google_calendar"
if google_calendar_service.present?
client.authorization.update_token! :access_token => google_calendar_service.token, :refresh_token => google_calendar_service.google_calendar_refresh_token, :expires_in => google_calendar_service.google_calendar_expires_in, :issued_at => Time.at(google_calendar_service.google_calendar_issued_at)
client.authorization.fetch_access_token! if client.authorization.expired?
end
client
end
services_controller.rb
def connect_google_calendar
#google_calendar_url = Service.google_calendar_client.authorization.authorization_uri.to_s
end
def create_google_calendar
client = Service.google_calendar_client
client.authorization.code = params[:code]
client.authorization.fetch_access_token!
current_user.services.create :provider => 'google_calendar', :token => client.authorization.access_token, :google_calendar_refresh_token => client.authorization.refresh_token, :google_calendar_expires_in => client.authorization.expires_in, :google_calendar_issued_at => client.authorization.issued_at
end
The truth is, I don't know. Your code looks right to me. But I can at least tell you what the error means. Missing authorization code means that it thinks you're trying to do an "authorization code" grant type when you fetch the access token. If you're actually trying to obtain an access token off a refresh token as opposed to doing it on the first pass after obtaining authorization from the user, then you may not have correctly set up the authorization object.
You can check this by inspecting the client.authorization.grant_type value. In very recent versions of the client you can manually set the grant_type value to force a particular mode, which may give you more informative error messages, depending on what the actual issue is.

Resources