How do I fetch user's Google Calendar persistently with Ruby? - ruby

My app already fetches Google Calendar but users has to login every time to get the new calendar. The issue raises from token being expired after a while.
I've already tried the following which is to include user.profile scope:
Rails.application.config.middleware.use OmniAuth::Builder do
provider :google_oauth2, GOOGLE_APP_KEY, GOOGLE_APP_SECRET, {
scope: 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/userinfo.profile',
redirect_uri: GOOGLE_APP_CALLBACK_URL
}
end
So, now the token persist so user doesn't have to login every single time to the app, but if the Google Calendar has new entry, my web app won't be able to fetch the new events because the token is expired.
What's the way to solve this problem?

I can't give you a complete answer, but I was working on similar functionality and found some documentation on using a refresh token
I also found an example of using that token to get a new access token here
the relevant code is:
client = OAuth2::Client.new(
"code", "secret",
:site => "https://accounts.google.com",
:token_url => "/o/oauth2/token",
:authorize_url => "/o/oauth2/auth")
access_token = OAuth2::AccessToken.from_hash(client,
{:refresh_token => current_user.refreshtoken})
access_token = access_token.refresh!
I believe you'll need to request an approval_prompt of offline for the refresh token

Related

How to get a refresh token when using the Google Calendar API Ruby Library?

I'm a newbie trying to implement the Google Calendar API into a web-based app and after following the instructions that they provide to the t, fetching information only works for about 20 minutes (while the access token is still valid). I understand that you need a refresh token in order to generate a new access token, but running this script from the terminal (which google provided in their documentation) doesn't provide a refresh token.
The code I executed in terminal:
google-api oauth-2-login --scope=https://www.googleapis.com/auth/calendar --client- id=CLIENT_ID --client-secret=CLIENT_SECRET
This generated a .yaml file with all of my keys which looks like this:
---
mechanism: oauth_2
scope: SCOPE_HERE
client_id: CLIENT_ID_HERE
client_secret: CLIENT_SECRET_HERE
access_token: ACCESS_TOKEN_HERE
refresh_token:
And the code that they provided if the access token expires:
oauth_yaml = YAML.load_file('.google-api.yaml')
client = Google::APIClient.new
client.authorization.client_id = oauth_yaml["client_id"]
client.authorization.client_secret = oauth_yaml["client_secret"]
client.authorization.scope = oauth_yaml["scope"]
client.authorization.refresh_token = oauth_yaml["refresh_token"]
client.authorization.access_token = oauth_yaml["access_token"]
if client.authorization.refresh_token && client.authorization.expired?
client.authorization.fetch_access_token!
end
service = client.discovered_api('calendar', 'v3')
So, according the yaml file, client.authorization.refresh_token is always 'nil', and it never gets a new access token. Also, client.authorization.expired? always returns false, even after the app has stopped working.
I've seen some other questions on here pertaining to the same issue, but since I'm generating my tokens via a terminal command, I'm not really sure how to go about getting that refresh token.
You need to specify that you want offline access to get a refresh token: access_type=offline
See https://developers.google.com/accounts/docs/OAuth2WebServer#offline

Google Developer API keys & YouTube API

I started a web based dev project using YouTube and when I signed up for the API all I received was a client ID and email ID. However in the Ruby example text for calling the data API it states:
def initialize(scope)
credentials = Google::APIClient::ClientSecrets.load
#authorization = Signet::OAuth2::Client.new(
:authorization_uri => credentials.authorization_uri,
:token_credential_uri => credentials.token_credential_uri,
:client_id => credentials.client_id,
:client_secret => credentials.client_secret,
:redirect_uri => credentials.redirect_uris.first,
:scope => scope
I don't know where to go to grab these credentials. Ideas?
Since YouTube is now Google-account-based, these credentials need to be acquired on the Google Developers Console (pick the YouTube Data API), and you'll have to initiate the normal Google sign-in process. Your client_id and client_secret are generated when you create a new application on the console, your redirect_uri is set based on your application structure.
While Signet has a nice OAuth2 module associated with it, I've found Omniauth to be very easy to work with (and you can still use it in conjunction with Signet). If you don't need to customize the sign in process, Omniauth puts most of this in a black box (it's very high-touch), and handles token refreshes, etc. for you. I might use the Omniauth Google strategy to get your tokens, then the Google Ruby client to make requests.

Integrating Google Plus with Rails

I already have the login (OAuth) piece working with my app, what I am trying to do now is pull down the authenticated users' activity list (status feed, for example).
The user has the option to pull this list down after the fact of them being authenticated and here is my code, thus far:
# User Model
def gplus
auth = authorizations.find_by_provider("gplus")
client = Google::APIClient.new(application_name: "AppName", application_version: '1.0', authorization: nil)
plus = client.discovered_api('plus', 'v1')
result = client.execute(
key: API["gplus"][Rails.env]["secret"],
api_method: plus.people.get,
parameters: { 'collection' => 'public', 'userId' => 'me' }
)
return result.data
end
Here is the problem I keep running into (from rails console)
#<Google::APIClient::Schema::Plus::V1::Person:0x3fe649ed04bc
DATA:{"error"=>{"errors"=>[{"domain"=>"usageLimits", "reason"=>"keyInvalid", "message"=>"Bad Request"}], "code"=>400, "message"=>"Bad Request"}}>
I am using the https://github.com/google/google-api-ruby-client... any reason why this won't work?
Code is close, but not quite there!
You're getting the auth object, but not actually passing it to your client there (you're setting it to nil).
You seem to be passing your client secret as the API key, which will cause problems. They API key is for a "simple API access" key from the API console - you don't need to pass anything if you're using an oAuth 2.0 token. If you'd like, you can pass a Server simple API key. This actually catches incorrectly using access tokens for a different project, so can be handy, but isn't required.
You don't need to specify a collection argument for plus.people.get
Additionally, make sure that the Google+ API is enabled under Services in the API console: http://developers.google.com/+

Unable to refresh token after expiration

I can authenticate and fetch an access_token and the corresponding refresh_token fine (subsequent API interactions are also fine).
However, I seem to only be able to refresh a token (POST to /oauth/token with grant_type=refresh_token) before the access_token actually expires. After the expiration, the same refresh code (exactly that provided within the docs), returns with an error of invalid_grant.
I am using the soundcloud-ruby SDK, FWIW, but I can reproduce it through curl.
As an aside, I found some old messages from the Google Group mentioning that I can request a non-expiring token, but I do not see this mentioned anywhere in the docs. Is this still a viable option?
That is correct. Refresh tokens cannot be used after an access token expires.
You can request a non-expiring access token by specifying scope=non-expiring when constructing an authorization URL. To do this with the Ruby SDK, simply pass the additional params to the authorize_url method:
require 'soundcloud'
client = Soundcloud.new(
:client_id => 'YOUR_CLIENT_ID',
:client_secret => 'YOUR_CLIENT_SECRET',
:redirect_uri => 'REDIRECT_URI'
)
client.authorize_url(:scope => 'non-expiring')
The rest of the flow should be exactly the same (grab the 'code' parameter from the query string and make a POST request to /oauth2/token).

Help Refreshing Yahoo's OAuth Access Token in Ruby

I'm at the point of involuntary hair loss while trying to refresh the Yahoo OAuth access token in Ruby.
Using the OmniAuth and OAuth gems, I'm able to get an access token from Yahoo, however it expires in one hour.
I'm following the Yahoo instructions to refresh an expired token, and am consistently returned a 401.
If someone could show me how to refresh the access token using the OAuth gem, I'd be greatly appreciative.
First, make sure you are saving your oauth_session_handle parameter from your original get_access_token call.
Then, when you are looking to refresh the access_token do something like this:
request_token = OAuth::RequestToken.new(consumer,
config["ACCESS_TOKEN"],
config["ACCESS_TOKEN_SECRET"])
token = OAuth::Token.new(config["ACCESS_TOKEN"],
config["ACCESS_TOKEN_SECRET"])
#access_token = request_token.get_access_token(
:oauth_session_handle => config["SESSION_HANDLE"],
:token => token)
... where ...
config["ACCESS_TOKEN"] is your old access token
config["ACCESS_TOKEN_SECRET"] is your old secret
config["SESSION_HANDLE"] is your oauth_session_handle
consumer is your OAuth::Consumer.new reference
I store the config variable in a yaml file and then load it on startup.
Remember to store the #access_token for next time.
I adapted this from an answer at YDN OAuth Forum.
Note: oauth_session_handle is returned as a param by the call to get_access_token:
access_token = request_token.get_access_token(:oauth_verifier => oauth_verifier)
oauth_session_handle = access_token.params['oauth_session_handle']
This was less than obvious from looking at the oauth-ruby/oauth code

Resources