I'm using the google-api-ruby-client gem in rails.
In the examples I found an example to load GooglePlus data:
client = Google::Apis::PlusV1::PlusService.new
client.key = ENV['GOOGLE_CLIENT_ID']
client.authorization = authorization
I have already obtained both refresh_token and the auth_token with another method (using devise oauth).
How can I generate the authorization object, starting from the tokens I have?
You can create the authorization object by instantiate a new UserRefreshCredentials like this:
authorization = Google::Auth::UserRefreshCredentials.new(
client_id: GOOGLE_CLIENT_ID
client_secret: GOOGLE_CLIENT_SECRET
scope: GOOGLE_SCOPE,
access_token: YOUR_AUTH_TOKEN,
refresh_token: YOUR_REFRESH_TOKEN,
expires_at: YOUR_EXPIRE_AT_TIMESTAMP,
grant_type: 'authorization_code')
Related
I'm trying to get my access token in OAuth2 so that I can access the Google Calendar API. Everywhere I look it says that i'm supposed to pass in a 'grant_type' that is equal to refresh_token but I keep getting the error
"undefined local variable or method `refresh_token' for # Did you mean? real_csrf_token"
So somewhere a long the line my 'refresh_token' is not being set. Please
events_controller.rb
auth = Signet::OAuth2::Client.new(
access_type: 'offline',
token_credential_uri: 'https://accounts.google.com/o/oauth2/token',
client_id: ENV['GOOGLE_CLIENT_ID'],
client_secret: ENV['GOOGLE_SECRET_KEY'],
grant_type: refresh_token
)
auth.fetch_access_token!
Also, I have an omniauth initializer file, which is where I believe i'm missing my opportunity to grab the access token and refresh token.
config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2, ENV['GOOGLE_CLIENT_ID'], ENV['GOOGLE_SECRET_KEY'],
{
scope: ['https://www.googleapis.com/auth/calendar', 'https://www.googleapis.com/auth/plus.login'],
skip_jwt: true
}
end
Any help is appreciated!
The documentation is telling you to pass a grant_type of 'refresh_token'. That's a string.
I have an app which syncs to OneDrive. If the user is using Office365 via GoDaddy and I have a grant_type of 'refresh_token', it doesn't return the refresh_token back, which in turn, won't let me refresh the token I currently have. I've tried adding access_type="offline" and prompt="consent" when doing a POST request to no avail. Help?
Here's my code:
credentials = OpenStruct.new
params = {
client_id: client_credentials[:key],
redirect_uri: redirect_url,
client_secret: client_credentials[:secret],
refresh_token: refresh_token,
grant_type: 'refresh_token',
resource: resource_id,
access_type: 'offline',
prompt: 'consent'
}
RestClient.post(client.token_url, params) # doesn't return refresh_token
Based on the request, it seems you were refresh the token. Based on the OAuth 2.0 code grant flow, there is no parameter about access_type and prompt. You can refer below for the support parameter:
And here is the post for your reference:
POST /{tenant}/oauth2/token HTTP/1.1
Host: https://login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&refresh_token=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq...
&grant_type=refresh_token
&resource=https%3A%2F%2Fservice.contoso.com%2F
&client_secret=JqQX2PNo9bpM0uEihUPzyrh
I am currently in Step 3 of the processing on getting an oauth token/secret from an user trying to login via Twitter. https://dev.twitter.com/docs/auth/implementing-sign-twitter
Step 3 tells me to send this request to the API, but I am stuck as to how to do so. I currently have BOTH the oauth_token and oauth_verifier, but how do I send this POST request to get the oauth_token, oauth_token_secret pair?
Is there a standard Oauth Ruby gem I can use to send this POST request? I see examples online where I pass an #accessToken object, but i do not have such an object available. I just have the oauth_token and oauth_verifier (as strings). Given these 2 things, how do I convert them to an oauth_token and oauth_token_secret?
POST /oauth/access_token HTTP/1.1
User-Agent: themattharris' HTTP Client
Host: api.twitter.com
Accept: */*
Authorization: OAuth oauth_consumer_key="cChZNFj6T5R0TigYB9yd1w",
oauth_nonce="a9900fe68e2573b27a37f10fbad6a755",
oauth_signature="39cipBtIOHEEnybAR4sATQTpl2I%3D",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1318467427",
oauth_token="NPcudxy0yU5T3tBzho7iCotZ3cnetKwcTIRlX0iwRl0",
oauth_version="1.0"
Content-Length: 57
Content-Type: application/x-www-form-urlencoded
oauth_verifier=uw7NjWHT6OJ1MpJOXsHfNxoAhPKpgI8BlYDhxEjIBY
Try something like the following rails controller actions, using the twitter and oauth gems:
def redirect
consumer = OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET, {
:site => "https://api.twitter.com",
:scheme => :header
})
request_token = consumer.get_request_token(:oauth_callback => CALLBACK_URL)
session[:twitter_request_token] = request_token
redirect_to request_token.authorize_url #=> "https://api.twitter.com/oauth/authorize?oauth_token=XYZ"
end
def callback
request_token = session[:twitter_request_token]
access_token = request_token.get_access_token(:oauth_verifier => params[:oauth_verifier])
client = Twitter::REST::Client.new(
:consumer_key => CONSUMER_KEY,
:consumer_secret => CONSUMER_SECRET,
:access_token => access_token.token,
:access_token_secret => access_token.secret
)
twitter_user = client.user
redirect_to root_url # or do something with the twitter_user
end
See also: http://barkingiguana.com/2009/10/13/twitter-oauth-authentication-using-ruby/
yes there is the Omniauth gem for authentication with Twitter. The documentation is straight forward.
I personally use Omniauth integrated with Devise and the Twitter gem to access Twitter - works very well.
Hope this helps,
Eugen
The common procedure is the following:
You shell to register your app on twitter development page.
Then set the proper Name, Description, and Website values up for your application.
App Name
App Description
http://your_app_domain.zone:3000/
Change Application Type is your app, by default it has read only access type.
Setup the callback URL for yuor application:
http://your_app_domain.zone:3000/auth/twitter/callback
Store the all keys, and secrets that are shewn on the OAuth tool twitter page:
Consumer key:
Consumer secret:
Access token:
Access token secret:
Setup route on your site with devise, or devise-like gem with the specified twitter keys, and secrets to enable authentication engine. The route list now shall include /auth/twitter path.
By going to http://your_app_domain.zone:3000/auth/twitter you will be redirected to twitter site, and dropped back to your site with passed oauth_token
But
You simple receive those keys, and secrets, and apply then in your app, avoiding the 6, and 7 points:
client = Twitter::REST::Client.new do |config|
config.consumer_key = "YOUR_CONSUMER_KEY"
config.consumer_secret = "YOUR_CONSUMER_SECRET"
config.access_token = "YOUR_ACCESS_TOKEN"
config.access_token_secret = "YOUR_ACCESS_SECRET"
end
After struggling with some SSL issues on my machine, I'm still trying to access a user's Blogger account through the Google Ruby Client API. I'm using the following:
Rails 3.2.3
Ruby 1.9.3
oauth2 (0.8.0)
omniauth (1.1.1)
omniauth-google-oauth2 (0.1.13)
google-api-client (0.4.6)
I can successfully authenticate users and access their blogs through the Google API at the time of authentication. When a user logs in, I store the access_token and refresh_token I receive from Google. and everything works great until the access_token expires. I'm trying to build the functionality that exchanges the refresh_token for a new access_token, but keep coming up against walls. Using the client documentation as an example, this is the code I'm using:
client = Google::APIClient.new
token_pair = auth.oauth_token # access_token and refresh_token received during authentication
# Load the access token if it's available
if token_pair
client.authorization.update_token!(token_pair.to_hash)
end
# Update access token if expired
if client.authorization.refresh_token && client.authorization.expired?
client.authorization.fetch_access_token!
end
blogger = client.discovered_api('blogger', 'v3')
result = client.execute(
api_method: blogger.blogs.list_by_user,
parameters: {'userId' => "self", 'fields' => 'items(description,id,name,url)'},
headers: {'Content-Type' => 'application/json'})
This code works perfectly while the access_token is valid. As soon as it expires though, I'm seeing 2 problems:
Even though I know the token is expired (I've checked expires_at value in the database), client.authorization.expired? returns false -- is there a different way I can check the expiration of the token besides using the value in the database?
When I force the execution of client.authorization.fetch_access_token! I get an invalid_request error.
Can someone please let me know how I can exchange a refresh_token for a new access_token using the client API? Even if you know how to do it in another language, that would be a big help as I can then try to Rubyfy it. Thanks!!
You may have already found this, but you can read through the whole process here at google: https://developers.google.com/accounts/docs/OAuth2WebServer
The omniauth-google-oauth2 strategy already takes care of setting the access_type and approval_prompt so getting a refresh token is just a matter of posting to https://accounts.google.com/o/oauth2/token with grant_type=request_token
Here is roughly the code I use:
def refresh_token
data = {
:client_id => GOOGLE_KEY,
:client_secret => GOOGLE_SECRET,
:refresh_token => REFRESH_TOKEN,
:grant_type => "refresh_token"
}
#response = ActiveSupport::JSON.decode(RestClient.post "https://accounts.google.com/o/oauth2/token", data)
if #response["access_token"].present?
# Save your token
else
# No Token
end
rescue RestClient::BadRequest => e
# Bad request
rescue
# Something else bad happened
end
Since you are using the Ruby Google API Client, why not use it to exchange the refresh token as well? The Ruby API does pretty much the same thing internally, which #brimil01 has said in his answer.
This is how I use the Ruby API to exchange my refresh token for a new access token.
def self.exchange_refresh_token( refresh_token )
client = Google::APIClient.new
client.authorization.client_id = CLIENT_ID
client.authorization.client_secret = CLIENT_SECRET
client.authorization.grant_type = 'refresh_token'
client.authorization.refresh_token = refresh_token
client.authorization.fetch_access_token!
client.authorization
end
And according to this issue here, it is recommended not to use the expired? method to check if an access token has expired.
Basically, don't call the expired? method. There are essentially zero
scenarios where that's a good idea. It simply won't give you reliable
expiration information. It's more of a hint than a real expiration
timestamp, and the token server may decide to honor an expired token
anyways in certain somewhat theoretical, but important, circumstances.
If you do get an invalid grant error, always refresh your access token
and retry once. If you're still getting an error, raise the error.
Here is what I do.
# Retrieved stored credentials for the provided user email address.
#
# #param [String] email_address
# User's email address.
# #return [Signet::OAuth2::Client]
# Stored OAuth 2.0 credentials if found, nil otherwise.
def self.get_stored_credentials(email_address)
hash = Thread.current['google_access_token']
return nil if hash.blank?
hash[email_address]
end
##
# Store OAuth 2.0 credentials in the application's database.
#
# #param [String] user_id
# User's ID.
# #param [Signet::OAuth2::Client] credentials
# OAuth 2.0 credentials to store.
def self.store_credentials(email_address, credentials)
Thread.current['google_access_token'] ||= {}
Thread.current['google_access_token'][email_address] = credentials
end
def self.credentials_expired?( credentials )
client = Google::APIClient.new
client.authorization = credentials
oauth2 = client.discovered_api('oauth2', 'v2')
result = client.execute!(:api_method => oauth2.userinfo.get)
(result.status != 200)
end
# #return [Signet::OAuth2::Client]
# OAuth 2.0 credentials containing an access and refresh token.
def self.get_credentials
email_address = ''
# Check if a valid access_token is already available.
credentials = get_stored_credentials( email_address )
# If not available, exchange the refresh_token to obtain a new access_token.
if credentials.blank?
credentials = exchange_refresh_token(REFRESH_TOKEN)
store_credentials(email_address, credentials)
else
are_credentials_expired = credentials_expired?(credentials)
if are_credentials_expired
credentials = exchange_refresh_token(REFRESH_TOKEN)
store_credentials(email_address, credentials)
end
end
credentials
end
I fixed it with simple code below.
def refesh_auth_tooken(refresh_token)
client = Google::APIClient.new
puts "REFESH TOOKEN"
client.authorization = client_secrets
client.authorization.refresh_token = refresh_token
#puts YAML::dump(client.authorization)
client.authorization.fetch_access_token!
return client.authorization
end
I've implemented a REST API and protected it with doorkeeper.
I've written a small client program to access it and it works fine using the resource owner credential flow.
Now I'm trying to implement a call using the client credentials flow. So I've followed the example in the link.
Everything works great when I'm using a GET request, but when I'm using a POST request, I'm getting a 401 Unauthorized. This is a call to a method that doesn't require a resource owner.
The only relevant thing I have in my API controller is:
doorkeeper_for :all
I haven't implemented any scopes or nothing of that kind (am I required to?).
My client code looks like this (exactly as in the example in github):
require 'rest-client'
require 'json'
client_id = 'my_client_id...'
client_secret = 'my_client_secret...'
response = RestClient.post 'http://localhost:3000/oauth/token', {
grant_type: 'client_credentials',
client_id: client_id,
client_secret: client_secret
}
token = JSON.parse(response)["access_token"]
# this line works great:
RestClient.get 'http://localhost:3000/api/v1/flights.json', { 'Authorization' => "Bearer #{token}" }
# this line always fails (401 Unauthorized):
RestClient.post 'http://localhost:3000/api/v1/flights.json', { 'Authorization' => "Bearer #{token}" }
Any idea what I may be doing wrong? Is there something special I should do in my application in order to enable the client credentials flow?
I figured it out. The problem was that I didn't use RestClient.post properly. The second parameter should be the payload and the third should be the header. It should be something like this:
RestClient.post 'http://localhost:3000/api/v1/flights.json', {}, { 'Authorization' => "Bearer #{token}" }