Google Oauth2 automatially renew token - ruby-on-rails

I'm adding Oauth2 via Google to my Rails app using the omniauth-google-oauth2 gem. I have everything set up according to the gem's readme. When I click the authorize button in my app, I am directed to Google to authorize the app. Once I'm sent back to my redirect path, I get a hash of data that includes the token, refresh_token, and expires_at (not expires_in), however expires_at is always equal to Time.now.to_i. The hash looks something like this:
=> auth.credentials
=> {
token: 'token',
refresh_token: 'refresh_token',
expires_at: 1468603958
expires: true
}
What I would like to do is programmatically renew the user's token every time they log into the app, without forcing them to go through the initial authorization process. I have a similar token renewal flow set up for Facebook, but I can't figure out how to make this work for Google, since the token expires_at time is now.
My question is two-part:
Shouldn't the value of expires_at be in the future (e.g. 1 hour from now, or 30 days from now)?
How can I programmatically refresh the token using the refresh_token that I got when I first authorized the app? I can't seem to find a single example in Ruby on how to do this. I wanted to set up a method on my GeneralController in my app that checks the expires_at value of my Identity model and then runs some logic to renew the token, but I can't figure out how to renew the token. This also seems problematic given the expires_at value I got from Google is equal to Time.now.to_i. If I uses that value, it would trigger an infinite loop.
Here's what I want do do in pseudo code:
class GeneralController < ApplicationController
before_action :renew_google_token, only: [:home]
def renew_google_token
# Get first Identity that will soon expire. This will be repeated unti lall identities are updated
current_user.identities.where(provider: :google).where("expires_at < (?)", Time.now + 7.days).each do |identity|
# Logic that pings Google and renews the token
new_token_hash = something
identity.update(token: new_token_hash.token, expires_at: Time.at(new_token_hash.expires_at))
end
end
end

Related

Authenticating docusign via Rails API (Omniauth + Devise) + JS Frontend

I'm trying to create an authentication flow using Auth Code Grant where I've added necessary omniauth strategy for Docusign to create /auth/docusign routes in Rails API only application.
Here are the steps followed
I'm issuing a request to the route from VueJS client.
window.open("http://localhost:4000/auth/docusign", "targetWindow", "width=350,height=250")
After user enters credentials and on successful login I'm calling the callback:
class SessionsController < Devise::SessionsController
def docusign
internal_destroy
#success = false
userinfo = request.env['omniauth.auth']
request_info = request.env['omniauth.params']
if userinfo
info = userinfo.info
cred = userinfo.credentials
user = User.find_by(email: info['email']) || User.find_by(id: session[:user_id])
if user
organization = user.organization
organization.organization_providers.where(provider_name: 'Docusign').destroy_all
OrganizationProvider.create(email: info['email'], token_expires_at: Time.at(cred['expires_at']), token_expires_at: Time.now, provider_name: 'Docusign', organization_id: organization.id, token: cred.token)
#success = true
end
end
render 'sessions/docusign'
end
end
I'd like to pass some params (which I'm accessing in the callback as request.env['omniauth.params']) for executing some backend tasks in the method.
When I try window.open("http://localhost:4000/auth/docusign?email='"+email+"'", "targetWindow", "width=350,height=250")
It says that the url doesn't match with any redirect urls
I have also tried passing in redirect_to('/auth/docusign', query: query) but on doing so, it doesn't open in a browser due to CORS.
I'm also trying to set it in session cookie, but since it's an API only server, I'm still working towards setting up cookie store.
Question
Which is the best way to achieve this? To pass some params in the callback and retrieve it.
Then the execution flow continues on the Rails server and the window serves a page with an appropriate response as per authentication status. However during this time, the client window which started the request is not aware of the authentication outcome.
Question
How can I communicate to the VueJS client that the authentication process is completed?
Question
Am I doing the above flow correctly or are there any better ways to achieve the same?
Thanks in advance
You need to log into your DocuSign Developer Account, Click on Admin and go on the left nav down to "API and Keys" where you can find the integration key you set. Did you set one?
If you did, you should find it and then add the redirectUri to the OAuth settings for that key (client ID in OAuth).
That is why DocuSign login tells you that the redirectURI doesn't match. You can add http://localhost:4000/auth to the list and that should work for your local env.
You cannot past custom variables on the redirectUri, it has to match exactly to the one you entered. If you need to pass values to it, there's a way to do that using state.
Here is how the URL should look, notice the &state= part of it:
https://account-d.docusign.com/oauth/auth?
response_type=code
&scope=YOUR_REQUESTED_SCOPES
&client_id=YOUR_INTEGRATION_KEY
&state=YOUR_CUSTOM_STATE
&redirect_uri=YOUR_REDIRECT_URI
&login_hint=YOUR_LOGIN_HINT
You can put whatever you want in there (URI encoded of course) and that value would come back to you when redirected back also with &state= parameter.
This solves the problem and allows you to pass arguments back to your redirect URI.

Where I can take correct API token for requests to Trello behalf on user (ruby-trello)

I am using Ruby on Rails 5.2 and gems: ruby-trello, devise, omniauth-trello.
I want to make authorized requests on behalf of Trello user same as shows here: https://github.com/jeremytregunna/ruby-trello#multiple-users
Example from git docs:
#client_bob = Trello::Client.new(
:consumer_key => YOUR_CONSUMER_KEY,
:consumer_secret => YOUR_CONSUMER_SECRET,
:oauth_token => "Bob's access token",
:oauth_token_secret => "Bob's access secret"
)
My steps:
User (Bob) sign in with Trello and get his own oauth_secret and oauth_token
App creates a Trello::Client for Bob using:
his own oauth data (:oauth_token, :oauth_token_secret)
I got consumer_key from here: https://trello.com/app-key (in the top of page, first block with key field)
consumer_secret was taken from https://trello.com/app-key too, but from the bottom of page, last block with key secret
After this, I'm trying to get any data from Bob's trello account (boards, lists, tasks etc.) but always getting 500 error (invalid token).
Could you explain what I'm doing wrong?
Thank you in advance.
What I did was implementing a session controller to request and authorize access to user's trello and then call Trello::Client with the authorization params inside the callback method on the controller.
Check out this:Trello OAuth 1.0 authorization with Ruby
Then inside the authorization method you can call Trello::Client using :oauth_token and :oauth_token_secret from get_access_token call or store them both on the session object and use them anywhere.

Where to start the Authentication request for Active Resource when working with OAuth

I am having trouble connecting a few dots with OAuth and Active Resource. Here is what I know: The Active Resource documentation tells me that I can set authentication tokens on the ActiveResource model itself like so:
ActiveResource::Base.connection.auth_type = :bearer
ActiveResource::Base.connection.bearer_token = #bearer_token
class Estimate < ActiveResource::Base
self.connection.auth_type = :bearer
self.connection.bearer_token = #bearer_token
self.site = "https://apistaging.uship.com/v2/estimate"
end
Also, in a totally seperate part of my code, I can retrieve the bearer token I need with the following basic HTTP requests (omitting my actual client id and secret for privacy sake):
uri = URI('https://apistaging.uship.com/oauth/token')
res = Net::HTTP.post_form(uri, 'grant_type' => 'client_credentials', 'client_id' => 'XXXXXXXXXXXXXXXXXXX', 'client_secret' => 'YYYYYYY')
When I print res, it gives me a valid token, which I have tested. It looks like this:
{
"access_token": "AAAAAAAAAAAAAAAAAAAAA",
"token_type": "bearer",
"expires_in": 600,
"refresh_token": "BBBBBBBBBBBBBBBBBBBBB"
}
But where do I run this call? I am guessing maybe some sort of before_filter on any controllers which will use Active Resource? And if so, how to I pass the access_token to that variable #bearer_token in my ActiveResource model. Also, I know that that access token will expire in 10 minutes, so somehow this code will have to know how to run the oauth token again, at the right times, or pass refresh tokens when Active Resource is being used in less than 10 minute intervals. I am very surprised to not find a simple tutorial for this online. If someone feels I should be using a gem to automate this, let me know which one, because everything I've found doesn't appear to work on rails 5.
If you extend ActiveResource like this https://gist.github.com/acherukuri/0f297e145b8242c2e991647c77f1a91e with https://github.com/oauth-xx/oauth-ruby, you would be able to make calls to your services using the OAuth gem instead of ActiveResource but the response can be handed over to ActiveResource so that you will still be able to rescue errors using ActiveResource::ServerError style in your controllers. In this way, your controller will not be polluted with the generation of OAuth tokens and also ActiveResource will generate a new OAuth token only if it's about to expire. (same token will be used for the subsequent calls until it gets expired)

Secure Handling of Auth Tokens with Rails

I'm working on a new rails api project, and I'm trying to get to the best usability/security balance.
The user will establish a session by posting username/password to api/v1/sessions. For a valid user, it will create an authentication token, which is it's own activerecord model, associated by the polymorphic relationship authenticatable, giving me the flexibility to have multiple user models if I need to.
class AuthenticationToken < ActiveRecord::Base
before_create :digest_token
def self.from_authenticatable(authenticatable)
self.create(authenticatable: authenticatable, token: generate_token)
end
end
The undigested token (SecureRandom.hex) gets rendered back to the client via JSON to authenticate future requests. The token gets salted and digested for storage in the database using BCrypt and a configured work factor.
The client will provide the raw auth token and an identity (can be username or password) to load the user record. Authentication will be handled by authenticate_with_http_token
def authenticate_token
authenticate_with_http_token do |token, options|
user = User.identified_by(options["identity"])
user.authentication_tokens.any? do |auth_token|
#current_user = user if auth_token.secure_compare(token)
end
end
end
Auth tokens are compared via a constant time alogrithm lifted from devise.
If current user is not successfully set, I render 401 unauthorized.
I feel like this is reasonably secure, and the only thing I really want to do is add some more fields to the auth token to track where it was last used (user agent) to allow the user to revoke some of the auth tokens if they like.
Are there any major holes with this approach? Anything else I should consider?

Playing with the Yahoo Fantasy Sports API in IRB

I want to play around with the Yahoo Fantasy Sports API. I have no idea where to start. What do I need to do in order to start playing with the API in IRB and start calling and retrieving different players or stats? This is my first attempt at tackling an API that does not have a readily available Ruby gem.
Yahoo Fantasy Sports API: http://developer.yahoo.com/fantasysports/guide/
I've followed the steps detailed in the dev guide and set up my developer Consumer key and Secret key. Not sure what to do with this information though.
I'm using Rails 3.2.2 and Ruby 1.9.2
I've been spending many hours the past couple weeks trying to get a site to tie in with the Yahoo fantasysports API and only recently got over the hurdle of being able to get authenticated through OAuth, make valid requests, and refresh access tokens indefinitely. Here are the steps you need to take to be able to mess around in IRB:
Gem stuff
Include oauth-plugin in your Gemfile. This will also install the OAuth/OAuth2 ruby gems as dependencies. This will handle the request/access tokens needed, but won't be fully compatible right out of the box.
The oauth-plugin requires an authentication system to be in place for your app. I would highly recommend devise both for its ease of use and the fact that the oauth-plugin pretty much works along with it with very little setup. I found it easier to connect the two if I first generate 'User' through devise before I generated a consumer with the oauth-plugin. There are tons of guides for devise if you run into issues.
Migration stuff
A quirk of the Yahoo version of OAuth is that you need to store a 'session_handle' for a user in order to refresh the access token when it expires after 60 minutes. I just edited the existing OauthConsumerToken migration to include t.string :session_handle. I'm not sure what the issue was with MYSQL when I did this, but I had to also specify a limit of 190 for the consumer_tokens index that oauth created. So my full add index is add_index :consumer_tokens, :token, :unique => true, :length => 190. I would recommend also adding a :guid string column to the users table, since that is what Yahoo uses as a unique identifier.
To accommodate the two extra columns we are tracking that oauth doesn't know about (session handle and guid), you need to override some of the oauth-plugin default behaviour. I've already forked the project and made the necessary changes if you want to just use my version (https://github.com/JamesSwift/oauth-plugin). The three dependencies for my version are a session_handle column in the ConsumerTokens table, a yahoo_guid column in the Users table, and set the variable CB_URL in your development.rb to be the url that you registered your app under with Yahoo. Also remember that if you use my version of the plugin you need to specify the path/github url depending on how you want to include it.
Configuration stuff
You need to set the :key and :secret in config/intializers/oauth_consumers.rb. I call my consumer a YahooToken, so mine looks like this:
OAUTH_CREDENTIALS={
:yahoo => {
:key => "the key given to me by yahoo"
:secret => "the secret given to me by yahoo"
}
}
load 'oauth/models/consumers/service_loader.rb'
You also need to specify the global yahoo settings in the custom token class you created. Again, mine is a YahooToken, so in app/models/yahoo_token.rb my first few lines are:
class YahooToken < ConsumerToken
YAHOO_SETTINGS={
:site=>"http://fantasysports.yahooapis.com/fantasy/v2",
:authorize_url =>"https://api.login.yahoo.com/oauth/v2/request_auth",
:request_token_url => "https://api.login.yahoo.com/oauth/v2/get_request_token",
:access_token_url => "https://api.login.yahoo.com/oauth/v2/get_token",
:oauth_version=>"1.0"
}
Tell your user model it has a token:
has_one :yahoo, :class_name=>"YahooToken", :dependent=>:destroy
Actually doing stuff, stuff
Now you can load up your server, create a user, and go to http://localhost:3000/oauth_consumers/yahoo to get your token. Yahoo refuses to redirect you back to localhost, so you will end up getting redirected to CB_URL/parameters-that-yahoo-returns. Copy the parameter string and go to http://localhost:3000/oauth_consumers/yahoo/callback/paste-the-string-here. This should successfully complete the initial retrieval of the access token. Don't worry, you only need to do this once per local user while developing locally. It automatically redirects in production environments.
Now that you have a token you can use it in IRB for the next hour as much as you want. Here is an example of using the token from the console:
t = User.first.yahoo
resp = t.client.get("http://fantasysports.yahooapis.com/fantasy/v2/users;use_login=1")
puts resp.body
You can also put &format=json at the end to get it in json instead of xml
After 60 minutes you need to get a new access token. I have a refresh function in my YahooToken:
def refresh!
old_one = self
request_token = OAuth::RequestToken.new(YahooToken.consumer, self.token, self.secret)
options={}
options[:oauth_session_handle]=self.session_handle
access_token = request_token.get_access_token options
new_one = YahooToken.find_or_create_from_access_token self.user, access_token
if new_one
old_one.delete
return new_one
end
return nil
end
Whenever my token expires I just t = t.refresh!. I would also recommend an age method on your tokens which will facilitate creating a rake task that will refresh tokens every hour for you automatically.
All the available fantasysports related resources are listed here:(http://developer.yahoo.com/fantasysports/guide/)
To get started, I would suggest you familiarize yourself with OAuth, which the Yahoo Fantasy Sports API uses for authentication. You will also need to know how to make HTTP requests in Ruby. Most of the rest of work will be in reading the API documentation and experimenting.
If you're looking for Ruby libraries for using OAuth or making HTTP requests, or anything else you run into, you may find The Ruby Toolbox helpful.
I prefer pelle/oauth-plugin save session_handle in refresh! rather then fork it.
consumer wrapper
model/YahooToken.rb inspired by #JamesSwift.
Migration
I authorizate with omniauth-yahoo, so the guid save in Authorization model and you need to add session_handle(:string) and change token(:text) column in consumer_tokens.
enjoy.

Resources