• Sean Hammond's avatar
    Fix refreshing access tokens when laptop suspends · 361d3381
    Sean Hammond authored
    This commit fixes an issue that refresh token requests were being sent
    too late if a laptop was suspended, even if the laptop was resumed again
    in time to do the refresh.
    
    For example if a laptop was suspended for 10 mins, then the refresh
    request would be sent 10 mins later than it should have been and the
    access token and refresh token would have already expired before the
    refresh request was attempted.
    
    The bug was due to a misunderstanding about how `window.setTimeout()`
    works:
    
    If you, say, use `setTimeout` to call a function in 10 minutes time,
    then wait 5 minutes, then put the laptop to sleep for an hour, then
    wake it up, **it will wait another 5 minutes and then call the
    function**.
    
    That is, the time that the laptop spent asleep does not count towards
    the timeout, it pauses the timer and then continues the timer again when
    the laptop wakes. This will also happen if the browser or OS suspends
    the tab or app.
    
    I've verified this behaviour in Chrome and Firefox on Linux, and it also
    agrees with [this StackOverflow
    answer](http://stackoverflow.com/questions/6346849/what-happens-to-settimeout-when-the-computer-goes-to-sleep/6347336#6347336)
    and [the HTML5 spec on
    `setTimeout`](https://www.w3.org/TR/2010/WD-html5-20101019/timers.html#timers):
    "wait until the Document associated with the method context has been
    fully active for a further timeout milliseconds (not necessarily
    consecutively)."
    
    This means that if you want to definitely call a function at a given
    time (say 55 mins in the future) you can't just use `setTimeout` to set
    a single timer for 55 mins like our code was doing - if the laptop is
    suspended during those 55 mins then the timer won't go off until 55 mins
    + however long the laptop was suspended for.
    
    To fix this change the code to repeatedly poll, every few seconds,
    whether the current time is later than the access token expiry time
    minus 5 minutes. If it is, then try to refresh the access token.
    
    I'm using repeated calls to `window.setTimeout()` to implement this,
    rather than one call to `window.setInterval()`, because with
    `setTimeout()` it's easier to do a couple of things that the previous
    implementation was already doing:
    
    1. Not make any more refresh token requests while one is still in flight
    2. Stop making further refresh token requests when one fails
    361d3381
oauth-auth.js 4.77 KB