I'm building a rails app that uses the Twitter Ruby gem to call the Twitter API. I've authorized the app myself so I get more than the normal 150 (I think I get 350) calls per hour, but in production this still may not be enough. What are my options to avoid rate-limiting besides caching (already doing it) and requiring the user to log in themselves?
You need to get the user oauth_token and the user oauth_token_secret, and then you do the requests on his behalf (so you don't have the limit).
So, assuming that you have the token and secret_token, you can do this:
#client_twitter = Twitter::Client.new(
:oauth_token => token,
:oauth_token_secret => secret_token
)
And just do the request with that #client_twitter. For example:
#client_twitter.profile_image(uid)
So, you might be asking: How do I get the oauth_token and oauth_token_secret? They need to give permissions to your app to do so. You can use OmniAuth, and you will see both the token and secret token in the hash that is coming back once they have authenticate.
Related
I have a Rails 5 API only app and an Angular JS Frontend app and would like to integrate with Asana API. I'm using the ruby-asana, omniauth and omniauth-asana gems.
I start the request using Asana's JS library like so:
var client = Asana.Client.create({
clientId: 172706773623703,
clientSecret: '<client_secret>',
redirectUri: '<redirect_url>'
});
client.useOauth({
flowType: Asana.auth.PopFlow
});
And the above does redirect me to Asana where I can login. On the redirectUri I'm giving a backend route (Rails 5 API only) which should handle the remaining on the authentication (using the JS only I get only a temporary token that cannot be self renewed meaning the user will have to authenticate every time the token expires. This is if I understood the documentation correctly).
So, on the controller I've created to handle the route, I have the following (from an example on Asana's documentation):
require 'omniauth-asana'
use OmniAuth::Strategies::Asana, <secret>, <secret>
creds = request.env["omniauth.auth"]["credentials"].tap { |h| h.delete('expires') }
strategy = request.env["omniauth.strategy"]
access_token = OAuth2::AccessToken.from_hash(strategy.client, creds).refresh!
$client = Asana::Client.new do |c|
c.authentication :oauth2, access_token
end
Now, the above doesn't work because 1) there's no request.env as this is an API only app, so I've followed the instruction on Omniauth and have added the following to my config/application.rb:
config.session_store :cookie_store, key: '_interslice_session'
config.middleware.use ActionDispatch::Cookies # Required for all session management
config.middleware.use ActionDispatch::Session::CookieStore, config.session_options
Now, in the request.headers I have _interslice_session which has some numbers. How can I create a Asana client with the above?
Any ideas?
OK, I think I see what you're attempting to do here; I think the best way forward is to start with how OAuth's Authorization Code Grant happens in general, then move into specifics for OmniAuth.
You send the user to a URL that Asana owns; that is, your goal is to get the user to visit a particular url. For Asana, this is https://app.asana.com/-/oauth_authorize. (Note that we respond with an error if you don't sent a correct client_id param, but feel free to check that link if you want). Do not send the client_secret during this request - it is intended to never be involved in client-side code, as this is insecure.
If they agree to give access, Asana sends back a redirect request to the user's browser with a short-lived code. That then means that your server will be called from the user's browser with this code as a parameter, so has to handle a new incoming request from the browser to whatever you specified as your redirect URI. Also, this location must be accessible by all users of your integration wherever they are.
You send this code from your server as a POST request to https://app.asana.com/-/oauth_token with your client_secret to Asana for a refresh token. This is where your application actually asks for credentials; the token given in the previous phases simply acknowledges that for a short time, the user has given your app permission to ask for these credentials, and your client_secret assures Asana that, for this server-side request, your app really is yours (it's like your application's password).
We send back an access_token which represents (approximately) a client-user credential pair that is valid for an hour.
You use these credentials to access our API on behalf of this user. We also send back a refresh_token which is long-lived, and used to get new short-lived access_tokens after they expire in a very similar way.
OK, so how this works with OmniAuth if I grok it correctly is that it expects to handle almost all of it. I'll be working through our omniauth example in our ruby-asana client library here: https://github.com/Asana/ruby-asana/blob/master/examples/omniauth_integration.rb
You set up OmniAuth with your client id and client secret
use OmniAuth::Strategies::Asana, <client_id>, <client_secret>
A request comes in, and you don't have credentials for it.
get '/' do
if $client
...
else
'sign in to asana'
end
end
The user clicks the sign in link, which (code omitted) sends them to the sign_in endpoint. This endpoint issues a redirect to /auth/asana
The browser requests /auth/asana from our server. If you look at that example, it's not implemented in our code. That's because the /auth/:provider is magically handled by OmniAuth.
This is where all the magic happens. OmniAuth handles the entire login flow above: send browser to our oauth_authorize above, then receive the callback and sticks the relevant params in the environment such that it knows "we just got the short lived code". By the time these lines get hit:
creds = request.env["omniauth.auth"]["credentials"].tap { |h| h.delete('expires') }
strategy = request.env["omniauth.strategy"]
you are inside a callback that OmniAuth has intercepted, gotten the needed creds, and set the creds in the environment. You shouldn't have to handle the oauth callback and token exchange manually.
Now, with the code you provided, I notice a few things right off:
You are causing the popup cycle to happen client side. It may be (and I strongly suspect) that this won't work with OmniAuth - it expects to handle the whole OAuth flow.
Based on the code snippet you provided, you aren't serving this out of a request-response cycle in the controller, rather, it appears that this is in the controller body and not out of an instance method. It may be a typo, but this needs to be in a method that is called back to outside of Rails (that is, a route must point to this a controller method that Asana can use to handle the browser request).
You shouldn't have to look at request.headers, I think - I'm not sure what the issues might be with request.env, but I suspect they may be unrelated to the API-only nature of your app. Are you sure that this is because it's API-only? After adding in the middleware, did you double-check that you can't access request.env? My hunch would be that persistent data in request.env will still be there, only it would require on the middleware being added in to do this. The instructions on OmniAuth simply say that you need to have a session store for your API - which makes sense to me, because APIs don't necessarily need to store state across requests, OmniAuth is telling you to put a session store back in.
I know this is a lot of info, but hopefully it helps you get on the right track. Cheers!
I am using accountright Live api v2 by MYOB. I want to get access token without going to login screen. When I send a CURL request to obtain access token i am redirected to myob login screen, how to skip that? The request I am sending is to url:
'https://secure.myob.com/oauth2/v2/authorize'
and params sent are:
Array
(
[client_id] => xxxxxxxxxxxxxxxxxxxxxxxx
[client_secret] => xxxxxxxxxxxxxxxxxxxxx
[scope] => CompanyFile
[code] => XXXXXXXXXXXXXX
[redirect_uri] => http://myappcodeonmydomain.com
[grant_type] => authorization_code
)
After your initial request to the API to get the access token, you should also be provided with a refresh token. Access tokens expire after a period of time, and need to be refreshed.
From the Refreshing an Access Token section in the Authentication Documentation:
Access tokens have a limited life span and when you receive one you'll
also receive an Expiry Time for it and a Refresh Token. Once your
access token expires it can no longer be used to access the API. So
you'll need to trigger a refresh. You do this by POSTing the following
parameters:
'client_id' // your API Key
'client_secret' // your API Secret
'refresh_token' // your refresh token
'grant_type' // this should say refresh_token
To this url: https://secure.myob.com/oauth2/v1/authorize
Note: while the data is formatted into a URL Query String you do not
pass the information via the URL (that would be a GET request), you
must pass the query string in the body and POST this to
https://secure.myob.com/oauth2/v1/authorize
As an example, I store my access and refresh tokens in a database, along with an expected expiry time 10 minutes in the future. If a request is going to be made after that time, I call the refresh procedure to update the access token, and am able to proceed on my merry way without needing to show the login prompt each time.
You do need to have it shown at least once to find out which user is logging in, and the GUID of the Company File to connect to.
If you are talking about the first time auth, then there is no way to do it. You have to redirect the user to the login page by returning the url.
If you are talking about refresh the token, then it's easy.
I'm not sure how you implement the API connection. I'm using the myob ruby sdk.
The ruby sdk is so easy to use and it will do all those auth operations for you.
:)
I am using FbGraph in my Ruby on Rails app along with Omniauth. I am using the server-side method to get the long-term (60 day) access token for the user and then storing it in a session variable. When trying to access the API in the following code:
token = session[:facebook_token]
user = FbGraph::User.me(token)
user = user.fetch
I get OAuthException :: Invalid OAuth access token. However when I add puts token and copy the printed token into the Facebook Graph API Explorer (online tool) I have no issues and the token is reported as valid ... I can even make the API call that's failing through FbGraph.
I was using the short term tokens from the JavaScript SDK (2 hour) and the same code was working fine so something about the move to the long term token is screwing everything up.
How are you setting session[:facebook_token] in the first place? It sounds like either a character encoding issue or perhaps even simply extra whitespace.
Why don't you try user = FbGraph::User.me(token.strip)
Also, you can update (extend) the access token itself like this: https://github.com/nov/fb_graph#extend-access-token-lifetime
I have a Rails (3.2.11) application that allows users to post updates to their LinkedIn profiles. I'm currently using the omniauth-linkedin gem to capture the initial user authentication and the linkedin gem to post the updates. The problem I'm having is that LinkedIn access tokens expire after 60 days, but according to their documentation a token can be refreshed prior to expiration without a user having to reauthorize the application.
I've looked at the LinkedIn Tips and Tricks, Authentication Overview, and tons of posts on StackOverflow - this, this, and this being just a couple of examples - and I still can't find any answers.
After a user authorizes the app (via omniauth-linkedin), I save the access_token and secret returned to me from LinkedIn. I need to figure out how I can use the still-valid access_token to refresh it and extend the expiration date another 60 days.
I've tried using the authenticate endpoint from LinkedIn (where tokens.access_token is the currently valid token):
url = "https//www.linkedin.com/uas/oauth/authenticate?oauth_token=" + tokens.access_token
result = RestClient.post(url, {oauth_callback: "http://localhost:3000/users/auth/linkedin/callback"})
but I get an undefined method 'request_uri' for #<URI::Generic:0x1b144d20> Exception.
I've tried using the OAuth::Consumer client (where tokens.access_token and tokens.token_secret are the currently valid tokens):
configuration = { site: 'https://api.linkedin.com', authorize_path: '/uas/oauth/authenticate',
request_token_path: '/uas/oauth/requestToken', access_token_path: '/uas/oauth/accessToken' }
consumer = OAuth::Consumer.new(ENV['LINKEDIN_APP_ID'], ENV['LINKEDIN_SECRET'], configuration)
access_token = OAuth::AccessToken.new(consumer, tokens.access_token, tokens.token_secret)
but this just gives me back the same access_token and secret.
In the end, I'd love to be able to leverage the existing omniauth-linkedin gem functionality to handle this refresh, any idea if this is possible? Thanks!
In your second approach (using the OAuth::Consumer client and passing in your existing access token and secret) should refresh the token for you. As the documentation states, as long as the current user is logged into LinkedIn.com and the current access token hasn't expired yet, the token will be refreshed.
That doesn't mean necessarily that you'll get a new token. You may get the same one as you had before. The key difference is that the lifespan of the token should 60 days. You can verify this by check the value of oauth_expires_in parameter. It should be set to 5184000.
This blog post goes into detail about refreshing the token: https://developer.linkedin.com/blog/tips-and-tricks-refreshing-access-token
I'm learning about OAuth with the goal of allowing visitors to my website the ability to sign in with Twitter. I've been using the Python based oauth2 library as a learning tool, and I think I get most of it.
I understand that after the user authenticates with the service (Twitter in this case) the user is sent to the callback URL with the parameters oauth_token and oauth_verifier.
What I fail to understand is the proper way of storing this information in the users browser. How do I identify these values during subsequent requests? Am I required to create a session system as with a normal website, or is there some magic in OAuth that makes this unnecessary?
How you handle client sessions of people who visit your website is not covered by OAuth, that remains up to you (and the usual session management frameworks).
All OAuth does is tell you that the user really is the Twitter user he claims to be. You can then associate this piece of information with the user session on your site (just like you would if the login screen was on your own page).
there are two types of oauth_token and oauth_verifier in twitter API
first is request token that always come different on each process, that can be save into session using getRequestToken method
i m telling in PHP view , but logic are same in any language
/
* Get request token */
$request_token = $connection->getRequestToken(OAUTH_CALLBACK);
/* Save request token to session */
$_SESSION['oauth_token'] = $token = $request_token['oauth_token'];
$_SESSION['oauth_token_secret'] = $request_token['oauth_token_secret'];
another is accesstoken: that is retrived via getAccessToken method
$access_token = $connection->getAccessToken($_REQUEST['oauth_verifier']);
Array
(
[oauth_token] => 223961574-mEctH7SHai######
[oauth_token_secret] => G7Buyxn4okF31Ln3ulAh#####
[user_id] => 223961574
[screen_name] => ltweetl
)
these token are same which is in your registered application on twitter
and already given at below page...
http://dev.twitter.com/apps/{your_app_id}/my_token.