Apologies for typos if any on my phone. We have been trying to put together a solid integration for several providers and aside from Twitter and their non existent email address (oh "good bye" unique key) we have Google with their extremely short token lifetime.
For now we resorted to performing a fake refresh by pushing user through flow on clientside in js.
How would one go about refreshing token without pushing user through the oauth flow without having Offline Access accessType? As refresh tokens are only valid for this accessType.
If i am missing a trick please let me know! All the social providers seem to follow different approaches as expiry does not seem to be exactly specified anywhere so in some cases it is a unixtime stamp some it is a negative integer in seconds relative to now (im guessing it has to be based on UTC or that wont work) and ive seen some that provide expiry as a unix timestamp. Damn was there no RFC for OAuth 2??
Any insights appreciated. Thank you.
Update
Apologies for the lack of clarity. Everything works, it's just Googles OAuth tokens are so shortlived. It's not a showstopper, it's just not ideal that we have to refresh Google's OAuth tokens with JS or use 'accessType' offline.
You don't say anything in your question about the specific OAuth flow that your app is using, so it's hard to provide a solid answer.
Two approaches tha spring to mind:-
If you're doing client JavaScript auth, then you can set immediate=true to the "refresh" is done without any user UI.
You can do the offline bit which wins you a refresh token. You could store that on a server and use it to generate access tokens as needed.
Related
I have two questions about Box's Oauth2 API in a testing environment.
Is it possible to have multiple redirect_URI addresses? I'd like to use one address for production (e.g., https://my_site.com/box_redirects_here), one for ongoing development (http://localhost:8000/box_redirects_here) and one for automatic UI tests (http://localhost:8001/box_redirects_here). As far as I could see, the only way to do that would be to create three different Box applications - is there an easier way? BTW, both Dropbox and Google Drive do support multiple redirect URIs.
I have a set of automatic tests that I'd like to run a few times a day. The challenge I'm facing is that every time I run these tests, my refresh_token is invalidated, and I can't use it again - which means I can't run the same set of tests a few hours later without manually getting a new token. One solution would be to save the refresh token, for example in a file, so I could reuse it across testing sessions. But:
It's really cumbersome.
if different developers are running these tests from different machines with no common file system that doesn't really work.
Again, for whatever reason this doesn't seem to be an issue with Google Drive or with Dropbox.
This is not currently possible, and I agree that would be nice.
Your best option is to save the access/refresh token pair to a file or a database (in the event that there's no common filesystem.) The OAuth2 spec grants implementers wide latitude on how they issue refresh tokens, if they issue them at all (I don't think Dropbox does.) While Box's implementation makes integration testing a bit challenging, I think that it ultimately hews most closely to the spec's recommendations.
For your first question, you might be able to get close to what you want by using the redirect_uri query parameter. Although you won't be able to supply an arbitrary redirect URI, you can give one that has the same base URL as the redirect URI in your app console.
From the OAuth tutorial:
Wildcard redirect_uri values are also accepted in the request as long as the base url matches the URI registered in the application console. A registered redirect_uri of https://www.myboxapp.com can be dynamically redirected to https://www.myboxapp.com/user1234 if passed into the request redirect_uri parameter.
For your second question, John is right - Box invalidates a refresh token after it has been used. Although this can be annoying, it's also more secure.
When I do the initial authentication to the Asana API with OAuth, it gives me a refresh_token as well as an expires_in of 3600 ms (1 hour).
What is the typical way of determining whether my app will need to use the refresh_token to get a new access_token?
I can think of two options:
Watch for an authentication error and then request a new access_token.
Store the date/time from now until 1 hour from now. Then with each API request, check the date/time to see if that time has already passed, and if so, request a new access_token.
Is there a better way than one of these two options? I don't see a recommendation from Asana in their OAuth documentation.
Thanks!
(I work at Asana.)
Great question! Both of your ideas are quite reasonable and should work - pick whichever is easiest / makes sense for your implementation. #1 is a little more robust in case there is skew with the timing, but if you just add some padding (be sure to request the token 1-2 minutes before expiration) then #2 should work just fine.
Note that it is always possible for the user to revoke the token, in which case when you go to get a new token the request will fail.
This seems like a good thing for us to recommend in the documentation, and possibly even add an automatic mechanism for in our client libraries.
We are:
Using OAuth for both authorization and authentication
Using the implicit-grant flow (i.e. client-side flow)
Issuing relatively short-lived access tokens (measured in hours, not weeks)
I want to offer a comparable experience to the traditional expiring-cookie policy, where you get a certain amount of time that your credentials will work, but if you stay active on the site that window continually resets. I'm realizing that this is not straightforward in OAuth. Yes, tokens are issued with an expiry time, but most implementations keep that time fixed regardless of activity.
This isn't so weird when issuing extremely long-lived tokens as one does when authorizing an integration (like, say, a Twitter app). But it's weird when using OAuth for user-facing authentication. If we issue a short-lived token and the user goes for a big session on our site, their access may run out abruptly even though they are in the middle of something. Even if we issued a token valid for 24 hours, if the user comes back 23.75 hours later, they're going to get 15 good minutes and then suddenly get kicked off.
I'm trying to figure out how to offer my users a better experience while sticking to OAuth mechanics. So far, my best idea is to change the server-side implementation to update the expiry date on the token on each authenticated request (this only works because our tokens have a server-side component - I don't know how it could work if we went the self-contained token route). And then, to keep the client appraised of the updated expiry time, we'll want to send that back with our response, maybe in a header, or a meta attribute in the JSON object.
This approach seems a little complicated, but workable. Is there a better way? Has anyone else dealt with this issue? Am I nuts for even trying?
I am a beginner in the world of Rails. Can someone please explain to me like I am a 2 year old. What in the world does token authentication do? Is it recommended for your app from a security standpoint or you are fine just without it???
Token authentication is usually used to create auto-signin links in your emails.
User clicks on link like http://example.com/some_page?auth_token=some-very-secret-token, which contains token and is recognized by token value.
So there is no need to remember password to unsubscribe from your site emails, for example.
Security tokens provide the "what you have" component in 2 factor / multi-factor solutions.
As talking to a two year old,
You want to get in the house. You would need a 'key' - a token that the system can identify you with.
I'm having trouble understanding OAuth2 conceptually. I've read about the whole handshake process a hundred times. I can login to my app using a google account, but once that's done, I need to access Google's API (read data from a Google Spreadsheet on that same account that I logged into, and whom I included spreadsheets in the :scope as per the strategy readme).
Currently, I'm using Omniauth and the omniauth-google-oauth2 strategy; this works great; it pulls up Google's authentication/login screen, and when I get back to my callback link, I'm storing [omniauth][credentials][token].
What is the best way to then use that token to do API work with Google Docs?
Is this the right approach?
I think of Oauth2 as a "way to get the user's password to confirm their existence on my site".
So instead of your User model having a password column, in essence, it uses Google to say "this guy is cool".
Now, what does that have to do with API calls, you wonder... me too.
If I recall, there is a Refresh token that lasts for more than the 20 ms of authetication and will allow you to access their Google Docs, if Google's api allows you to do that.
Having said all that, If google needs their token, plus your API token to access their spreadsheet, I'd stick it into the session.
But if their API said to stick spreadsheet in the scope, then it must say something about how to use it all together too, no?
More Edits
Google Spreadsheets Oauth 2.0 authentication piece is here, with a flow. Notice the part about refresh tokens. I'd look into that.
It says to store it somewhere, which I'd choose the session, or if you are totally paranoid a db column somewhere, but not sure if that is right either. Just spitballing here.
Final Edit
Turns out even the people helping out the Oauth 2.0 don't agree/get it conceptually either.
You may be able to find a gem that wraps the Google API to simplify your tasks.
Here's one that works with Google Drive and spreadsheets.
The google-drive-ruby gem that #Galen mentions seems to work nicely with the google-oauth-2 provider:
Guessing you're already storing the token in the session in your callback handler, e.g.
auth = request.env["omniauth.auth"]
session[:token] = auth["credentials"]["token"]
then you can use it to build a session and access the sheet:
require 'googleauth'
session = GoogleDrive::Session.from_access_token(token)
worksheet = session.spreadsheet_by_key(spreadsheet_id).worksheet_by_title(worksheet_name)
...etc
Hope this helps.