New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
http wrapper to automatically refresh token when talking to google #522
Conversation
The way this works is that it makes the request, if it receives a 401 back it then refreshes the token and resends the request. There was a suggestion that we use the seconds given when we receive the token, but I thought this was more reliable than assuming that the token was good or bad. Google sends the 401 if it is bad and that way it is caught no matter why it is bad (revoked auth...) |
@avital @scottburch When authenticating against Google, together with the accessToken, a number of seconds is given when the accessToken expires - is the resulting time (now + timeoutSeconds) saved somewhere? Because the refreshToken does only need to be used once the accesToken is not valid any more, hence when requests are made while it is still valid, numerous requests to get a new accessToken through the refreshToken can be saved. |
I'm not convinced that this is true. I only get a new accessToken if I receive a 401. Otherwise, i return the response from google. |
If I use the timeout, then what happens if authorization gets revoked for some other reason. The code would break. |
@scottburch are these answers targeted at my comment? If yes, I don't get them - my comment is talking about when the refreshToken needs to be used. The piece of code that determines whether the accessToken needs to be refreshed would also get a new accessToken if the token has been otherwise revoked. The only reason the time is needed is, to determine whether it even makes sense to try and use the accesToken at all. |
Yes, my comment is regarding your comment. What am I missing? I guess I was confused by your comment. How does the accessToken timeout affect the requests that are made? Is this just a requirement for your code? How does it affect THIS pull request? |
@scottburch sorry for the confusion, @avital told me to carry this comment over from #464, which has been closed already. |
And to clarify - the current pull request is always making a request, right? In X% of the cases this might not be necessary though (exactly, when we are still within time X and X+deltaValiditySeconds) because the accessToken does not need to be updated, because it is still valid. Only if its not valid any more (after X+deltaValiditySeconds) it needs to be refreshed. So storing the delta somewhere would not only make sense for my use case, but also for this pull request here. It would be an optimization. |
Read my comments. I don't think the code works that way. It is not 2X. It only updates the token when it is needed. |
I read your comments. I just don't think its a good idea to make that initial request. The lifetime is given for a reason. It is given so you don't need to make that first request. Requests are expensive, if there is a way save the overhead doing one, it should be done in my eyes. |
Sorry, I think I am the one that is causing confusion here here. The pull request is perfectly valid. It just makes the request whenever a 401 came back. My initial comment was added because I want to use the accessToken to access a GMail account with it. It has nothing to do with an HTTP request. Therefore I need to know the time when the accessToken expires, hence I want the lifetime (or more specifically now()+liefetime) to be saved, so I can just use the accessToken and only refresh it if its expired before accessing GMail via IMAP. I don't think this pull request is the right place for this. Sorry again. |
No problem. Glad it is clear. I'm doing the same thing with IMAP. I don't think that the OAUTH token is necessary for IMAP. I have not had any problems with that. |
@scottburch Sorry, for this slight offtopic question, but how are you generating the XOAuth2 token to pass on to GMail xoauth2 if not with the accessToken or refreshToken? |
I think we need more than this. Use case is: I want to use Google or Facebook APIs but let this package manage the logins. I'm doing this now, but managing the refresh in my code that uses Google APIs...but OAuth is supposed to make the tokens expire automatically for any service... |
Is there anything blocking this being merged? I think this is a crucial missing feature and I always use @scottburch's code in my meteor apps to refresh tokens. Would be great if this was built in. |
I would also like this to be merged. @scottburch @kynan Since this isn't merged yet, do I have to download the source code for meteor and replace packages/accounts-oauth2-helper/oauth2_server.js with Scott's code in order to use it? |
@ninajlu You can also integrate the relevant bits of @scottburch's code into your app. That's what I've been doing. |
@kynan are you integrating this with recent version of meteor? if so, have gist? A while back, Avital referenced this PR on meteor-talk - looking for an updated PR https://groups.google.com/d/msg/meteor-talk/fyFy3hsZVA4/zABQUDo_aGQJ I've got an app with problem with expired token accessing Google API, so I'm looking for solution on this to get into Meteor, or a package for now that adds the functionality. |
@ryw I've been using Meteor 0.6.5 but I'm not on the Meteor team and have no say on what gets integrated and what doesn't. I'm only lobbying for this to get merged since I think it's a really useful feature. I'm using the last 4 functions from @scottburch's pull request and whenever a Google OAuth call fails I check for a 401. If I get one, I call |
@kynan ah so you are handling the 401 within your app? Here's the code I use to read future events from Google Calendar + create objects in my project — would love to see how your code could handle the 401 + refresh token. Meteor.startup ->
Meteor.methods
importGoogleEvents: (userId) ->
email = Meteor.user().services.google.email
token = Meteor.user().services.google.accessToken
date = (new Date()).toISOString()
url = "https://www.googleapis.com/calendar/v3/calendars/"
url += "#{email}/events?timeMin=#{date}&access_token=#{token}"
Meteor.http.get url, (err, res) ->
params = {}
if err
console.log err
else
console.log "imported events for " + userId
Events.remove userId: userId
_.each res.data.items, (element, index, events) ->
params.googleId = element.id
params.status = element.status
params.location = element.location
params.start = element.start.dateTime
params.end = element.end.dateTime
params.url = element.htmlLink
params.title = element.summary
params.attendees = element.attendees
params.allDay = false
params.userId = userId
Events.insert params |
@ryw Like this: Meteor.startup ->
Meteor.methods
refreshOAuthToken: (service) ->
getNewAccessToken = (service) ->
result = Meteor.http.post(service.url, {headers: {'Content-Type': 'application/x-www-form-urlencoded'}, content: oAuthRefreshBody(service)})
return result.data?.access_token
oAuthRefreshBody = (service) ->
loginServiceConfig = Accounts.loginServiceConfiguration.findOne({service: service.name});
return 'refresh_token=' + Meteor.user().services[service.name].refreshToken +
'&client_id=' + loginServiceConfig.clientId +
'&client_secret=' + loginServiceConfig.secret +
'&grant_type=refresh_token'
storeNewAccessToken = (service, newAccessToken) ->
o = {}
o['services.' + service.name + '.accessToken'] = newAccessToken
Meteor.users.update Meteor.userId(), {$set: o}
token = getNewAccessToken service
console.log "Got new access token #{token} for", service
storeNewAccessToken service, token
return token
importGoogleEvents: (userId, token) ->
email = Meteor.user().services.google.email
token = Meteor.user().services.google.accessToken
date = (new Date()).toISOString()
url = "https://www.googleapis.com/calendar/v3/calendars/"
url += "#{email}/events?timeMin=#{date}&access_token=#{token}"
Meteor.http.get url, (err, res) ->
params = {}
if !err
console.log "imported events for " + userId
Events.remove userId: userId
_.each res.data.items, (element, index, events) ->
params.googleId = element.id
params.status = element.status
params.location = element.location
params.start = element.start.dateTime
params.end = element.end.dateTime
params.url = element.htmlLink
params.title = element.summary
params.attendees = element.attendees
params.allDay = false
params.userId = userId
Events.insert params
else if err.response.statusCode == 401
# Refresh OAuth token if it has expired. Simplified version of
# https://github.com/meteor/meteor/pull/522
Meteor.call 'refreshOAuthToken',
{name: 'google', url: 'https://accounts.google.com/o/oauth2/token'},
(err, token) ->
importGoogleEvents userId, token if !err
console.log err if err
else
console.log err |
@kynan thanks Florian - just implemented the code, need to wait for an expiration :) |
Not working for me yet... see Error:
My code:
/cc cfly15 |
@ryw Haven't seen this one before, but maybe you haven't requested the offline permission for your app? That's required for being able to use refresh tokens. |
Yay!
Trick was to set
|
Is this still on the agenda to be merged? |
Well, the PR appears to be out of date, for one (it doesn't merge cleanly). |
@glasser no surprise after more than 2 years! |
I don't think this is likely to be merged, and seems like something that could easily be a package anyway. |
As an FYI on this, my solution was to create a package that managed the access tokens of various providers. |
…g-2.14.0 Update validate-commit-msg to the latest version 🚀
Example use: